/**
 * TinyMCE version 7.7.1 (2025-03-05)
 */

(function() {
  "use strict";

  var global$1 = tinymce.util.Tools.resolve("tinymce.ModelManager");

  const hasProto = (v, constructor, predicate) => {
    var _a;
    if (predicate(v, constructor.prototype)) {
      return true;
    } else {
      return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;
    }
  };
  const typeOf = x => {
    const t = typeof x;
    if (x === null) {
      return "null";
    } else if (t === "object" && Array.isArray(x)) {
      return "array";
    } else if (t === "object" && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {
      return "string";
    } else {
      return t;
    }
  };
  const isType$1 = type => value => typeOf(value) === type;
  const isSimpleType = type => value => typeof value === type;
  const eq$2 = t => a => t === a;
  const isString = isType$1("string");
  const isObject = isType$1("object");
  const isArray = isType$1("array");
  const isNull = eq$2(null);
  const isBoolean = isSimpleType("boolean");
  const isUndefined = eq$2(undefined);
  const isNullable = a => a === null || a === undefined;
  const isNonNullable = a => !isNullable(a);
  const isFunction = isSimpleType("function");
  const isNumber = isSimpleType("number");

  const noop = () => {
  };
  const compose = (fa, fb) => {
    return (...args) => {
      return fa(fb.apply(null, args));
    };
  };
  const compose1 = (fbc, fab) => a => fbc(fab(a));
  const constant = value => {
    return () => {
      return value;
    };
  };
  const identity = x => {
    return x;
  };
  const tripleEquals = (a, b) => {
    return a === b;
  };

  function curry(fn, ...initialArgs) {
    return (...restArgs) => {
      const all = initialArgs.concat(restArgs);
      return fn.apply(null, all);
    };
  }

  const not = f => t => !f(t);
  const die = msg => {
    return () => {
      throw new Error(msg);
    };
  };
  const apply = f => {
    return f();
  };
  const never = constant(false);
  const always = constant(true);

  class Optional {
    constructor(tag, value) {
      this.tag = tag;
      this.value = value;
    }

    static some(value) {
      return new Optional(true, value);
    }

    static none() {
      return Optional.singletonNone;
    }

    static from(value) {
      return isNonNullable(value) ? Optional.some(value) : Optional.none();
    }

    fold(onNone, onSome) {
      if (this.tag) {
        return onSome(this.value);
      } else {
        return onNone();
      }
    }

    isSome() {
      return this.tag;
    }

    isNone() {
      return !this.tag;
    }

    map(mapper) {
      if (this.tag) {
        return Optional.some(mapper(this.value));
      } else {
        return Optional.none();
      }
    }

    bind(binder) {
      if (this.tag) {
        return binder(this.value);
      } else {
        return Optional.none();
      }
    }

    exists(predicate) {
      return this.tag && predicate(this.value);
    }

    forall(predicate) {
      return !this.tag || predicate(this.value);
    }

    filter(predicate) {
      if (!this.tag || predicate(this.value)) {
        return this;
      } else {
        return Optional.none();
      }
    }

    getOr(replacement) {
      return this.tag ? this.value : replacement;
    }

    or(replacement) {
      return this.tag ? this : replacement;
    }

    getOrThunk(thunk) {
      return this.tag ? this.value : thunk();
    }

    orThunk(thunk) {
      return this.tag ? this : thunk();
    }

    getOrDie(message) {
      if (!this.tag) {
        throw new Error(message !== null && message !== void 0 ? message : "Called getOrDie on None");
      } else {
        return this.value;
      }
    }

    getOrNull() {
      return this.tag ? this.value : null;
    }

    getOrUndefined() {
      return this.value;
    }

    each(worker) {
      if (this.tag) {
        worker(this.value);
      }
    }

    toArray() {
      return this.tag ? [this.value] : [];
    }

    toString() {
      return this.tag ? `some(${this.value})` : "none()";
    }
  }

  Optional.singletonNone = new Optional(false);

  const nativeSlice = Array.prototype.slice;
  const nativeIndexOf = Array.prototype.indexOf;
  const nativePush = Array.prototype.push;
  const rawIndexOf = (ts, t) => nativeIndexOf.call(ts, t);
  const contains$2 = (xs, x) => rawIndexOf(xs, x) > -1;
  const exists = (xs, pred) => {
    for (let i = 0, len = xs.length; i < len; i++) {
      const x = xs[i];
      if (pred(x, i)) {
        return true;
      }
    }
    return false;
  };
  const range$1 = (num, f) => {
    const r = [];
    for (let i = 0; i < num; i++) {
      r.push(f(i));
    }
    return r;
  };
  const map$1 = (xs, f) => {
    const len = xs.length;
    const r = new Array(len);
    for (let i = 0; i < len; i++) {
      const x = xs[i];
      r[i] = f(x, i);
    }
    return r;
  };
  const each$2 = (xs, f) => {
    for (let i = 0, len = xs.length; i < len; i++) {
      const x = xs[i];
      f(x, i);
    }
  };
  const eachr = (xs, f) => {
    for (let i = xs.length - 1; i >= 0; i--) {
      const x = xs[i];
      f(x, i);
    }
  };
  const partition = (xs, pred) => {
    const pass = [];
    const fail = [];
    for (let i = 0, len = xs.length; i < len; i++) {
      const x = xs[i];
      const arr = pred(x, i) ? pass : fail;
      arr.push(x);
    }
    return {
      pass,
      fail
    };
  };
  const filter$2 = (xs, pred) => {
    const r = [];
    for (let i = 0, len = xs.length; i < len; i++) {
      const x = xs[i];
      if (pred(x, i)) {
        r.push(x);
      }
    }
    return r;
  };
  const foldr = (xs, f, acc) => {
    eachr(xs, (x, i) => {
      acc = f(acc, x, i);
    });
    return acc;
  };
  const foldl = (xs, f, acc) => {
    each$2(xs, (x, i) => {
      acc = f(acc, x, i);
    });
    return acc;
  };
  const findUntil = (xs, pred, until) => {
    for (let i = 0, len = xs.length; i < len; i++) {
      const x = xs[i];
      if (pred(x, i)) {
        return Optional.some(x);
      } else if (until(x, i)) {
        break;
      }
    }
    return Optional.none();
  };
  const find$1 = (xs, pred) => {
    return findUntil(xs, pred, never);
  };
  const findIndex = (xs, pred) => {
    for (let i = 0, len = xs.length; i < len; i++) {
      const x = xs[i];
      if (pred(x, i)) {
        return Optional.some(i);
      }
    }
    return Optional.none();
  };
  const flatten = xs => {
    const r = [];
    for (let i = 0, len = xs.length; i < len; ++i) {
      if (!isArray(xs[i])) {
        throw new Error("Arr.flatten item " + i + " was not an array, input: " + xs);
      }
      nativePush.apply(r, xs[i]);
    }
    return r;
  };
  const bind$2 = (xs, f) => flatten(map$1(xs, f));
  const forall = (xs, pred) => {
    for (let i = 0, len = xs.length; i < len; ++i) {
      const x = xs[i];
      if (pred(x, i) !== true) {
        return false;
      }
    }
    return true;
  };
  const reverse = xs => {
    const r = nativeSlice.call(xs, 0);
    r.reverse();
    return r;
  };
  const mapToObject = (xs, f) => {
    const r = {};
    for (let i = 0, len = xs.length; i < len; i++) {
      const x = xs[i];
      r[String(x)] = f(x, i);
    }
    return r;
  };
  const sort$1 = (xs, comparator) => {
    const copy = nativeSlice.call(xs, 0);
    copy.sort(comparator);
    return copy;
  };
  const get$d = (xs, i) => i >= 0 && i < xs.length ? Optional.some(xs[i]) : Optional.none();
  const head = xs => get$d(xs, 0);
  const last$2 = xs => get$d(xs, xs.length - 1);
  const findMap = (arr, f) => {
    for (let i = 0; i < arr.length; i++) {
      const r = f(arr[i], i);
      if (r.isSome()) {
        return r;
      }
    }
    return Optional.none();
  };

  const keys = Object.keys;
  const hasOwnProperty = Object.hasOwnProperty;
  const each$1 = (obj, f) => {
    const props = keys(obj);
    for (let k = 0, len = props.length; k < len; k++) {
      const i = props[k];
      const x = obj[i];
      f(x, i);
    }
  };
  const map = (obj, f) => {
    return tupleMap(obj, (x, i) => ({
      k: i,
      v: f(x, i)
    }));
  };
  const tupleMap = (obj, f) => {
    const r = {};
    each$1(obj, (x, i) => {
      const tuple = f(x, i);
      r[tuple.k] = tuple.v;
    });
    return r;
  };
  const objAcc = r => (x, i) => {
    r[i] = x;
  };
  const internalFilter = (obj, pred, onTrue, onFalse) => {
    each$1(obj, (x, i) => {
      (pred(x, i) ? onTrue : onFalse)(x, i);
    });
  };
  const filter$1 = (obj, pred) => {
    const t = {};
    internalFilter(obj, pred, objAcc(t), noop);
    return t;
  };
  const mapToArray = (obj, f) => {
    const r = [];
    each$1(obj, (value, name) => {
      r.push(f(value, name));
    });
    return r;
  };
  const values = obj => {
    return mapToArray(obj, identity);
  };
  const get$c = (obj, key) => {
    return has$1(obj, key) ? Optional.from(obj[key]) : Optional.none();
  };
  const has$1 = (obj, key) => hasOwnProperty.call(obj, key);
  const hasNonNullableKey = (obj, key) => has$1(obj, key) && obj[key] !== undefined && obj[key] !== null;
  const isEmpty = r => {
    for (const x in r) {
      if (hasOwnProperty.call(r, x)) {
        return false;
      }
    }
    return true;
  };

  const Global = typeof window !== "undefined" ? window : Function("return this;")();

  const path = (parts, scope) => {
    let o = scope !== undefined && scope !== null ? scope : Global;
    for (let i = 0; i < parts.length && o !== undefined && o !== null; ++i) {
      o = o[parts[i]];
    }
    return o;
  };
  const resolve$2 = (p, scope) => {
    const parts = p.split(".");
    return path(parts, scope);
  };

  const unsafe = (name, scope) => {
    return resolve$2(name, scope);
  };
  const getOrDie = (name, scope) => {
    const actual = unsafe(name, scope);
    if (actual === undefined || actual === null) {
      throw new Error(name + " not available on this browser");
    }
    return actual;
  };

  const getPrototypeOf = Object.getPrototypeOf;
  const sandHTMLElement = scope => {
    return getOrDie("HTMLElement", scope);
  };
  const isPrototypeOf = x => {
    const scope = resolve$2("ownerDocument.defaultView", x);
    return isObject(x) && (sandHTMLElement(scope).prototype.isPrototypeOf(x) || /^HTML\w*Element$/.test(getPrototypeOf(x).constructor.name));
  };

  const COMMENT = 8;
  const DOCUMENT = 9;
  const DOCUMENT_FRAGMENT = 11;
  const ELEMENT = 1;
  const TEXT = 3;

  const name = element => {
    const r = element.dom.nodeName;
    return r.toLowerCase();
  };
  const type = element => element.dom.nodeType;
  const isType = t => element => type(element) === t;
  const isComment = element => type(element) === COMMENT || name(element) === "#comment";
  const isHTMLElement = element => isElement(element) && isPrototypeOf(element.dom);
  const isElement = isType(ELEMENT);
  const isText = isType(TEXT);
  const isDocument = isType(DOCUMENT);
  const isDocumentFragment = isType(DOCUMENT_FRAGMENT);
  const isTag = tag => e => isElement(e) && name(e) === tag;

  const rawSet = (dom, key, value) => {
    if (isString(value) || isBoolean(value) || isNumber(value)) {
      dom.setAttribute(key, value + "");
    } else {
      console.error("Invalid call to Attribute.set. Key ", key, ":: Value ", value, ":: Element ", dom);
      throw new Error("Attribute value was not simple");
    }
  };
  const set$2 = (element, key, value) => {
    rawSet(element.dom, key, value);
  };
  const setAll$1 = (element, attrs) => {
    const dom = element.dom;
    each$1(attrs, (v, k) => {
      rawSet(dom, k, v);
    });
  };
  const setOptions = (element, attrs) => {
    each$1(attrs, (v, k) => {
      v.fold(() => {
        remove$7(element, k);
      }, value => {
        rawSet(element.dom, k, value);
      });
    });
  };
  const get$b = (element, key) => {
    const v = element.dom.getAttribute(key);
    return v === null ? undefined : v;
  };
  const getOpt = (element, key) => Optional.from(get$b(element, key));
  const remove$7 = (element, key) => {
    element.dom.removeAttribute(key);
  };
  const clone$2 = element => foldl(element.dom.attributes, (acc, attr) => {
    acc[attr.name] = attr.value;
    return acc;
  }, {});

  const fromHtml$1 = (html, scope) => {
    const doc = scope || document;
    const div = doc.createElement("div");
    div.innerHTML = html;
    if (!div.hasChildNodes() || div.childNodes.length > 1) {
      const message = "HTML does not have a single root node";
      console.error(message, html);
      throw new Error(message);
    }
    return fromDom$1(div.childNodes[0]);
  };
  const fromTag = (tag, scope) => {
    const doc = scope || document;
    const node = doc.createElement(tag);
    return fromDom$1(node);
  };
  const fromText = (text, scope) => {
    const doc = scope || document;
    const node = doc.createTextNode(text);
    return fromDom$1(node);
  };
  const fromDom$1 = node => {
    if (node === null || node === undefined) {
      throw new Error("Node cannot be null or undefined");
    }
    return { dom: node };
  };
  const fromPoint$1 = (docElm, x, y) => Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom$1);
  const SugarElement = {
    fromHtml: fromHtml$1,
    fromTag,
    fromText,
    fromDom: fromDom$1,
    fromPoint: fromPoint$1
  };

  const is$2 = (element, selector) => {
    const dom = element.dom;
    if (dom.nodeType !== ELEMENT) {
      return false;
    } else {
      const elem = dom;
      if (elem.matches !== undefined) {
        return elem.matches(selector);
      } else if (elem.msMatchesSelector !== undefined) {
        return elem.msMatchesSelector(selector);
      } else if (elem.webkitMatchesSelector !== undefined) {
        return elem.webkitMatchesSelector(selector);
      } else if (elem.mozMatchesSelector !== undefined) {
        return elem.mozMatchesSelector(selector);
      } else {
        throw new Error("Browser lacks native selectors");
      }
    }
  };
  const bypassSelector = dom => dom.nodeType !== ELEMENT && dom.nodeType !== DOCUMENT && dom.nodeType !== DOCUMENT_FRAGMENT || dom.childElementCount === 0;
  const all$1 = (selector, scope) => {
    const base = scope === undefined ? document : scope.dom;
    return bypassSelector(base) ? [] : map$1(base.querySelectorAll(selector), SugarElement.fromDom);
  };
  const one = (selector, scope) => {
    const base = scope === undefined ? document : scope.dom;
    return bypassSelector(base) ? Optional.none() : Optional.from(base.querySelector(selector)).map(SugarElement.fromDom);
  };

  const eq$1 = (e1, e2) => e1.dom === e2.dom;
  const contains$1 = (e1, e2) => {
    const d1 = e1.dom;
    const d2 = e2.dom;
    return d1 === d2 ? false : d1.contains(d2);
  };
  const is$1 = is$2;

  const owner = element => SugarElement.fromDom(element.dom.ownerDocument);
  const documentOrOwner = dos => isDocument(dos) ? dos : owner(dos);
  const documentElement = element => SugarElement.fromDom(documentOrOwner(element).dom.documentElement);
  const defaultView = element => SugarElement.fromDom(documentOrOwner(element).dom.defaultView);
  const parent = element => Optional.from(element.dom.parentNode).map(SugarElement.fromDom);
  const parentElement = element => Optional.from(element.dom.parentElement).map(SugarElement.fromDom);
  const parents = (element, isRoot) => {
    const stop = isFunction(isRoot) ? isRoot : never;
    let dom = element.dom;
    const ret = [];
    while (dom.parentNode !== null && dom.parentNode !== undefined) {
      const rawParent = dom.parentNode;
      const p = SugarElement.fromDom(rawParent);
      ret.push(p);
      if (stop(p) === true) {
        break;
      } else {
        dom = rawParent;
      }
    }
    return ret;
  };
  const prevSibling = element => Optional.from(element.dom.previousSibling).map(SugarElement.fromDom);
  const nextSibling = element => Optional.from(element.dom.nextSibling).map(SugarElement.fromDom);
  const children$2 = element => map$1(element.dom.childNodes, SugarElement.fromDom);
  const child$2 = (element, index) => {
    const cs = element.dom.childNodes;
    return Optional.from(cs[index]).map(SugarElement.fromDom);
  };
  const firstChild = element => child$2(element, 0);

  const before$3 = (marker, element) => {
    const parent$1 = parent(marker);
    parent$1.each(v => {
      v.dom.insertBefore(element.dom, marker.dom);
    });
  };
  const after$5 = (marker, element) => {
    const sibling = nextSibling(marker);
    sibling.fold(() => {
      const parent$1 = parent(marker);
      parent$1.each(v => {
        append$1(v, element);
      });
    }, v => {
      before$3(v, element);
    });
  };
  const prepend = (parent, element) => {
    const firstChild$1 = firstChild(parent);
    firstChild$1.fold(() => {
      append$1(parent, element);
    }, v => {
      parent.dom.insertBefore(element.dom, v.dom);
    });
  };
  const append$1 = (parent, element) => {
    parent.dom.appendChild(element.dom);
  };
  const appendAt = (parent, element, index) => {
    child$2(parent, index).fold(() => {
      append$1(parent, element);
    }, v => {
      before$3(v, element);
    });
  };
  const wrap = (element, wrapper) => {
    before$3(element, wrapper);
    append$1(wrapper, element);
  };

  const after$4 = (marker, elements) => {
    each$2(elements, (x, i) => {
      const e = i === 0 ? marker : elements[i - 1];
      after$5(e, x);
    });
  };
  const append = (parent, elements) => {
    each$2(elements, x => {
      append$1(parent, x);
    });
  };

  const empty = element => {
    element.dom.textContent = "";
    each$2(children$2(element), rogue => {
      remove$6(rogue);
    });
  };
  const remove$6 = element => {
    const dom = element.dom;
    if (dom.parentNode !== null) {
      dom.parentNode.removeChild(dom);
    }
  };
  const unwrap = wrapper => {
    const children = children$2(wrapper);
    if (children.length > 0) {
      after$4(wrapper, children);
    }
    remove$6(wrapper);
  };

  const clone$1 = (original, isDeep) => SugarElement.fromDom(original.dom.cloneNode(isDeep));
  const shallow = original => clone$1(original, false);
  const deep = original => clone$1(original, true);
  const shallowAs = (original, tag) => {
    const nu = SugarElement.fromTag(tag);
    const attributes = clone$2(original);
    setAll$1(nu, attributes);
    return nu;
  };
  const copy$2 = (original, tag) => {
    const nu = shallowAs(original, tag);
    const cloneChildren = children$2(deep(original));
    append(nu, cloneChildren);
    return nu;
  };
  const mutate$1 = (original, tag) => {
    const nu = shallowAs(original, tag);
    after$5(original, nu);
    const children = children$2(original);
    append(nu, children);
    remove$6(original);
    return nu;
  };

  const validSectionList = [
    "tfoot",
    "thead",
    "tbody",
    "colgroup"
  ];
  const isValidSection = parentName => contains$2(validSectionList, parentName);
  const grid = (rows, columns) => ({
    rows,
    columns
  });
  const address = (row, column) => ({
    row,
    column
  });
  const detail = (element, rowspan, colspan) => ({
    element,
    rowspan,
    colspan
  });
  const detailnew = (element, rowspan, colspan, isNew) => ({
    element,
    rowspan,
    colspan,
    isNew
  });
  const extended = (element, rowspan, colspan, row, column, isLocked) => ({
    element,
    rowspan,
    colspan,
    row,
    column,
    isLocked
  });
  const rowdetail = (element, cells, section) => ({
    element,
    cells,
    section
  });
  const rowdetailnew = (element, cells, section, isNew) => ({
    element,
    cells,
    section,
    isNew
  });
  const elementnew = (element, isNew, isLocked) => ({
    element,
    isNew,
    isLocked
  });
  const rowcells = (element, cells, section, isNew) => ({
    element,
    cells,
    section,
    isNew
  });
  const bounds = (startRow, startCol, finishRow, finishCol) => ({
    startRow,
    startCol,
    finishRow,
    finishCol
  });
  const columnext = (element, colspan, column) => ({
    element,
    colspan,
    column
  });
  const colgroup = (element, columns) => ({
    element,
    columns
  });

  const isShadowRoot = dos => isDocumentFragment(dos) && isNonNullable(dos.dom.host);
  const getRootNode = e => SugarElement.fromDom(e.dom.getRootNode());
  const getContentContainer = dos => isShadowRoot(dos) ? dos : SugarElement.fromDom(documentOrOwner(dos).dom.body);
  const getShadowRoot = e => {
    const r = getRootNode(e);
    return isShadowRoot(r) ? Optional.some(r) : Optional.none();
  };
  const getShadowHost = e => SugarElement.fromDom(e.dom.host);
  const getOriginalEventTarget = event => {
    if (isNonNullable(event.target)) {
      const el = SugarElement.fromDom(event.target);
      if (isElement(el) && isOpenShadowHost(el)) {
        if (event.composed && event.composedPath) {
          const composedPath = event.composedPath();
          if (composedPath) {
            return head(composedPath);
          }
        }
      }
    }
    return Optional.from(event.target);
  };
  const isOpenShadowHost = element => isNonNullable(element.dom.shadowRoot);

  const inBody = element => {
    const dom = isText(element) ? element.dom.parentNode : element.dom;
    if (dom === undefined || dom === null || dom.ownerDocument === null) {
      return false;
    }
    const doc = dom.ownerDocument;
    return getShadowRoot(SugarElement.fromDom(dom)).fold(() => doc.body.contains(dom), compose1(inBody, getShadowHost));
  };
  const body$1 = () => getBody$1(SugarElement.fromDom(document));
  const getBody$1 = doc => {
    const b = doc.dom.body;
    if (b === null || b === undefined) {
      throw new Error("Body is not available yet");
    }
    return SugarElement.fromDom(b);
  };

  const ancestors$4 = (scope, predicate, isRoot) => filter$2(parents(scope, isRoot), predicate);
  const children$1 = (scope, predicate) => filter$2(children$2(scope), predicate);
  const descendants$1 = (scope, predicate) => {
    let result = [];
    each$2(children$2(scope), x => {
      if (predicate(x)) {
        result = result.concat([x]);
      }
      result = result.concat(descendants$1(x, predicate));
    });
    return result;
  };

  const ancestors$3 = (scope, selector, isRoot) => ancestors$4(scope, e => is$2(e, selector), isRoot);
  const children = (scope, selector) => children$1(scope, e => is$2(e, selector));
  const descendants = (scope, selector) => all$1(selector, scope);

  var ClosestOrAncestor = (is, ancestor, scope, a, isRoot) => {
    if (is(scope, a)) {
      return Optional.some(scope);
    } else if (isFunction(isRoot) && isRoot(scope)) {
      return Optional.none();
    } else {
      return ancestor(scope, a, isRoot);
    }
  };

  const ancestor$2 = (scope, predicate, isRoot) => {
    let element = scope.dom;
    const stop = isFunction(isRoot) ? isRoot : never;
    while (element.parentNode) {
      element = element.parentNode;
      const el = SugarElement.fromDom(element);
      if (predicate(el)) {
        return Optional.some(el);
      } else if (stop(el)) {
        break;
      }
    }
    return Optional.none();
  };
  const closest$2 = (scope, predicate, isRoot) => {
    const is = (s, test) => test(s);
    return ClosestOrAncestor(is, ancestor$2, scope, predicate, isRoot);
  };
  const child$1 = (scope, predicate) => {
    const pred = node => predicate(SugarElement.fromDom(node));
    const result = find$1(scope.dom.childNodes, pred);
    return result.map(SugarElement.fromDom);
  };
  const descendant$1 = (scope, predicate) => {
    const descend = node => {
      for (let i = 0; i < node.childNodes.length; i++) {
        const child = SugarElement.fromDom(node.childNodes[i]);
        if (predicate(child)) {
          return Optional.some(child);
        }
        const res = descend(node.childNodes[i]);
        if (res.isSome()) {
          return res;
        }
      }
      return Optional.none();
    };
    return descend(scope.dom);
  };

  const ancestor$1 = (scope, selector, isRoot) => ancestor$2(scope, e => is$2(e, selector), isRoot);
  const child = (scope, selector) => child$1(scope, e => is$2(e, selector));
  const descendant = (scope, selector) => one(selector, scope);
  const closest$1 = (scope, selector, isRoot) => {
    const is = (element, selector) => is$2(element, selector);
    return ClosestOrAncestor(is, ancestor$1, scope, selector, isRoot);
  };

  const is = (lhs, rhs, comparator = tripleEquals) => lhs.exists(left => comparator(left, rhs));
  const cat = arr => {
    const r = [];
    const push = x => {
      r.push(x);
    };
    for (let i = 0; i < arr.length; i++) {
      arr[i].each(push);
    }
    return r;
  };
  const bindFrom = (a, f) => a !== undefined && a !== null ? f(a) : Optional.none();
  const someIf = (b, a) => b ? Optional.some(a) : Optional.none();

  const removeFromStart = (str, numChars) => {
    return str.substring(numChars);
  };

  const checkRange = (str, substr, start) => substr === "" || str.length >= substr.length && str.substr(start, start + substr.length) === substr;
  const removeLeading = (str, prefix) => {
    return startsWith(str, prefix) ? removeFromStart(str, prefix.length) : str;
  };
  const contains = (str, substr, start = 0, end) => {
    const idx = str.indexOf(substr, start);
    if (idx !== -1) {
      return isUndefined(end) ? true : idx + substr.length <= end;
    } else {
      return false;
    }
  };
  const startsWith = (str, prefix) => {
    return checkRange(str, prefix, 0);
  };
  const endsWith = (str, suffix) => {
    return checkRange(str, suffix, str.length - suffix.length);
  };
  const blank = r => s => s.replace(r, "");
  const trim = blank(/^\s+|\s+$/g);
  const isNotEmpty = s => s.length > 0;
  const toFloat = value => {
    const num = parseFloat(value);
    return isNaN(num) ? Optional.none() : Optional.some(num);
  };

  const isSupported = dom => dom.style !== undefined && isFunction(dom.style.getPropertyValue);

  const internalSet = (dom, property, value) => {
    if (!isString(value)) {
      console.error("Invalid call to CSS.set. Property ", property, ":: Value ", value, ":: Element ", dom);
      throw new Error("CSS value must be a string: " + value);
    }
    if (isSupported(dom)) {
      dom.style.setProperty(property, value);
    }
  };
  const internalRemove = (dom, property) => {
    if (isSupported(dom)) {
      dom.style.removeProperty(property);
    }
  };
  const set$1 = (element, property, value) => {
    const dom = element.dom;
    internalSet(dom, property, value);
  };
  const setAll = (element, css) => {
    const dom = element.dom;
    each$1(css, (v, k) => {
      internalSet(dom, k, v);
    });
  };
  const get$a = (element, property) => {
    const dom = element.dom;
    const styles = window.getComputedStyle(dom);
    const r = styles.getPropertyValue(property);
    return r === "" && !inBody(element) ? getUnsafeProperty(dom, property) : r;
  };
  const getUnsafeProperty = (dom, property) => isSupported(dom) ? dom.style.getPropertyValue(property) : "";
  const getRaw$2 = (element, property) => {
    const dom = element.dom;
    const raw = getUnsafeProperty(dom, property);
    return Optional.from(raw).filter(r => r.length > 0);
  };
  const remove$5 = (element, property) => {
    const dom = element.dom;
    internalRemove(dom, property);
    if (is(getOpt(element, "style").map(trim), "")) {
      remove$7(element, "style");
    }
  };
  const copy$1 = (source, target) => {
    const sourceDom = source.dom;
    const targetDom = target.dom;
    if (isSupported(sourceDom) && isSupported(targetDom)) {
      targetDom.style.cssText = sourceDom.style.cssText;
    }
  };

  const getAttrValue = (cell, name, fallback = 0) => getOpt(cell, name).map(value => parseInt(value, 10)).getOr(fallback);
  const getSpan = (cell, type) => getAttrValue(cell, type, 1);
  const hasColspan = cellOrCol => {
    if (isTag("col")(cellOrCol)) {
      return getAttrValue(cellOrCol, "span", 1) > 1;
    } else {
      return getSpan(cellOrCol, "colspan") > 1;
    }
  };
  const hasRowspan = cell => getSpan(cell, "rowspan") > 1;
  const getCssValue = (element, property) => parseInt(get$a(element, property), 10);
  const minWidth = constant(10);
  const minHeight = constant(10);

  const firstLayer = (scope, selector) => {
    return filterFirstLayer(scope, selector, always);
  };
  const filterFirstLayer = (scope, selector, predicate) => {
    return bind$2(children$2(scope), x => {
      if (is$2(x, selector)) {
        return predicate(x) ? [x] : [];
      } else {
        return filterFirstLayer(x, selector, predicate);
      }
    });
  };

  const lookup = (tags, element, isRoot = never) => {
    if (isRoot(element)) {
      return Optional.none();
    }
    if (contains$2(tags, name(element))) {
      return Optional.some(element);
    }
    const isRootOrUpperTable = elm => is$2(elm, "table") || isRoot(elm);
    return ancestor$1(element, tags.join(","), isRootOrUpperTable);
  };
  const cell = (element, isRoot) => lookup([
    "td",
    "th"
  ], element, isRoot);
  const cells$1 = ancestor => firstLayer(ancestor, "th,td");
  const columns$1 = ancestor => {
    if (is$2(ancestor, "colgroup")) {
      return children(ancestor, "col");
    } else {
      return bind$2(columnGroups(ancestor), columnGroup => children(columnGroup, "col"));
    }
  };
  const table = (element, isRoot) => closest$1(element, "table", isRoot);
  const rows$1 = ancestor => firstLayer(ancestor, "tr");
  const columnGroups = ancestor => table(ancestor).fold(constant([]), table => children(table, "colgroup"));

  const fromRowsOrColGroups = (elems, getSection) => map$1(elems, row => {
    if (name(row) === "colgroup") {
      const cells = map$1(columns$1(row), column => {
        const colspan = getAttrValue(column, "span", 1);
        return detail(column, 1, colspan);
      });
      return rowdetail(row, cells, "colgroup");
    } else {
      const cells = map$1(cells$1(row), cell => {
        const rowspan = getAttrValue(cell, "rowspan", 1);
        const colspan = getAttrValue(cell, "colspan", 1);
        return detail(cell, rowspan, colspan);
      });
      return rowdetail(row, cells, getSection(row));
    }
  });
  const getParentSection = group => parent(group).map(parent => {
    const parentName = name(parent);
    return isValidSection(parentName) ? parentName : "tbody";
  }).getOr("tbody");
  const fromTable$1 = table => {
    const rows = rows$1(table);
    const columnGroups$1 = columnGroups(table);
    const elems = [
      ...columnGroups$1,
      ...rows
    ];
    return fromRowsOrColGroups(elems, getParentSection);
  };
  const fromPastedRows = (elems, section) => fromRowsOrColGroups(elems, () => section);

  const cached = f => {
    let called = false;
    let r;
    return (...args) => {
      if (!called) {
        called = true;
        r = f.apply(null, args);
      }
      return r;
    };
  };

  const DeviceType = (os, browser, userAgent, mediaMatch) => {
    const isiPad = os.isiOS() && /ipad/i.test(userAgent) === true;
    const isiPhone = os.isiOS() && !isiPad;
    const isMobile = os.isiOS() || os.isAndroid();
    const isTouch = isMobile || mediaMatch("(pointer:coarse)");
    const isTablet = isiPad || !isiPhone && isMobile && mediaMatch("(min-device-width:768px)");
    const isPhone = isiPhone || isMobile && !isTablet;
    const iOSwebview = browser.isSafari() && os.isiOS() && /safari/i.test(userAgent) === false;
    const isDesktop = !isPhone && !isTablet && !iOSwebview;
    return {
      isiPad: constant(isiPad),
      isiPhone: constant(isiPhone),
      isTablet: constant(isTablet),
      isPhone: constant(isPhone),
      isTouch: constant(isTouch),
      isAndroid: os.isAndroid,
      isiOS: os.isiOS,
      isWebView: constant(iOSwebview),
      isDesktop: constant(isDesktop)
    };
  };

  const firstMatch = (regexes, s) => {
    for (let i = 0; i < regexes.length; i++) {
      const x = regexes[i];
      if (x.test(s)) {
        return x;
      }
    }
    return undefined;
  };
  const find = (regexes, agent) => {
    const r = firstMatch(regexes, agent);
    if (!r) {
      return {
        major: 0,
        minor: 0
      };
    }
    const group = i => {
      return Number(agent.replace(r, "$" + i));
    };
    return nu$2(group(1), group(2));
  };
  const detect$5 = (versionRegexes, agent) => {
    const cleanedAgent = String(agent).toLowerCase();
    if (versionRegexes.length === 0) {
      return unknown$2();
    }
    return find(versionRegexes, cleanedAgent);
  };
  const unknown$2 = () => {
    return nu$2(0, 0);
  };
  const nu$2 = (major, minor) => {
    return {
      major,
      minor
    };
  };
  const Version = {
    nu: nu$2,
    detect: detect$5,
    unknown: unknown$2
  };

  const detectBrowser$1 = (browsers, userAgentData) => {
    return findMap(userAgentData.brands, uaBrand => {
      const lcBrand = uaBrand.brand.toLowerCase();
      return find$1(browsers, browser => {
        var _a;
        return lcBrand === ((_a = browser.brand) === null || _a === void 0 ? void 0 : _a.toLowerCase());
      }).map(info => ({
        current: info.name,
        version: Version.nu(parseInt(uaBrand.version, 10), 0)
      }));
    });
  };

  const detect$4 = (candidates, userAgent) => {
    const agent = String(userAgent).toLowerCase();
    return find$1(candidates, candidate => {
      return candidate.search(agent);
    });
  };
  const detectBrowser = (browsers, userAgent) => {
    return detect$4(browsers, userAgent).map(browser => {
      const version = Version.detect(browser.versionRegexes, userAgent);
      return {
        current: browser.name,
        version
      };
    });
  };
  const detectOs = (oses, userAgent) => {
    return detect$4(oses, userAgent).map(os => {
      const version = Version.detect(os.versionRegexes, userAgent);
      return {
        current: os.name,
        version
      };
    });
  };

  const normalVersionRegex = /.*?version\/\ ?([0-9]+)\.([0-9]+).*/;
  const checkContains = target => {
    return uastring => {
      return contains(uastring, target);
    };
  };
  const browsers = [
    {
      name: "Edge",
      versionRegexes: [/.*?edge\/ ?([0-9]+)\.([0-9]+)$/],
      search: uastring => {
        return contains(uastring, "edge/") && contains(uastring, "chrome") && contains(uastring, "safari") && contains(uastring, "applewebkit");
      }
    },
    {
      name: "Chromium",
      brand: "Chromium",
      versionRegexes: [
        /.*?chrome\/([0-9]+)\.([0-9]+).*/,
        normalVersionRegex
      ],
      search: uastring => {
        return contains(uastring, "chrome") && !contains(uastring, "chromeframe");
      }
    },
    {
      name: "IE",
      versionRegexes: [
        /.*?msie\ ?([0-9]+)\.([0-9]+).*/,
        /.*?rv:([0-9]+)\.([0-9]+).*/
      ],
      search: uastring => {
        return contains(uastring, "msie") || contains(uastring, "trident");
      }
    },
    {
      name: "Opera",
      versionRegexes: [
        normalVersionRegex,
        /.*?opera\/([0-9]+)\.([0-9]+).*/
      ],
      search: checkContains("opera")
    },
    {
      name: "Firefox",
      versionRegexes: [/.*?firefox\/\ ?([0-9]+)\.([0-9]+).*/],
      search: checkContains("firefox")
    },
    {
      name: "Safari",
      versionRegexes: [
        normalVersionRegex,
        /.*?cpu os ([0-9]+)_([0-9]+).*/
      ],
      search: uastring => {
        return (contains(uastring, "safari") || contains(uastring, "mobile/")) && contains(uastring, "applewebkit");
      }
    }
  ];
  const oses = [
    {
      name: "Windows",
      search: checkContains("win"),
      versionRegexes: [/.*?windows\ nt\ ?([0-9]+)\.([0-9]+).*/]
    },
    {
      name: "iOS",
      search: uastring => {
        return contains(uastring, "iphone") || contains(uastring, "ipad");
      },
      versionRegexes: [
        /.*?version\/\ ?([0-9]+)\.([0-9]+).*/,
        /.*cpu os ([0-9]+)_([0-9]+).*/,
        /.*cpu iphone os ([0-9]+)_([0-9]+).*/
      ]
    },
    {
      name: "Android",
      search: checkContains("android"),
      versionRegexes: [/.*?android\ ?([0-9]+)\.([0-9]+).*/]
    },
    {
      name: "macOS",
      search: checkContains("mac os x"),
      versionRegexes: [/.*?mac\ os\ x\ ?([0-9]+)_([0-9]+).*/]
    },
    {
      name: "Linux",
      search: checkContains("linux"),
      versionRegexes: []
    },
    {
      name: "Solaris",
      search: checkContains("sunos"),
      versionRegexes: []
    },
    {
      name: "FreeBSD",
      search: checkContains("freebsd"),
      versionRegexes: []
    },
    {
      name: "ChromeOS",
      search: checkContains("cros"),
      versionRegexes: [/.*?chrome\/([0-9]+)\.([0-9]+).*/]
    }
  ];
  const PlatformInfo = {
    browsers: constant(browsers),
    oses: constant(oses)
  };

  const edge = "Edge";
  const chromium = "Chromium";
  const ie = "IE";
  const opera = "Opera";
  const firefox = "Firefox";
  const safari = "Safari";
  const unknown$1 = () => {
    return nu$1({
      current: undefined,
      version: Version.unknown()
    });
  };
  const nu$1 = info => {
    const current = info.current;
    const version = info.version;
    const isBrowser = name => () => current === name;
    return {
      current,
      version,
      isEdge: isBrowser(edge),
      isChromium: isBrowser(chromium),
      isIE: isBrowser(ie),
      isOpera: isBrowser(opera),
      isFirefox: isBrowser(firefox),
      isSafari: isBrowser(safari)
    };
  };
  const Browser = {
    unknown: unknown$1,
    nu: nu$1,
    edge: constant(edge),
    chromium: constant(chromium),
    ie: constant(ie),
    opera: constant(opera),
    firefox: constant(firefox),
    safari: constant(safari)
  };

  const windows = "Windows";
  const ios = "iOS";
  const android = "Android";
  const linux = "Linux";
  const macos = "macOS";
  const solaris = "Solaris";
  const freebsd = "FreeBSD";
  const chromeos = "ChromeOS";
  const unknown = () => {
    return nu({
      current: undefined,
      version: Version.unknown()
    });
  };
  const nu = info => {
    const current = info.current;
    const version = info.version;
    const isOS = name => () => current === name;
    return {
      current,
      version,
      isWindows: isOS(windows),
      isiOS: isOS(ios),
      isAndroid: isOS(android),
      isMacOS: isOS(macos),
      isLinux: isOS(linux),
      isSolaris: isOS(solaris),
      isFreeBSD: isOS(freebsd),
      isChromeOS: isOS(chromeos)
    };
  };
  const OperatingSystem = {
    unknown,
    nu,
    windows: constant(windows),
    ios: constant(ios),
    android: constant(android),
    linux: constant(linux),
    macos: constant(macos),
    solaris: constant(solaris),
    freebsd: constant(freebsd),
    chromeos: constant(chromeos)
  };

  const detect$3 = (userAgent, userAgentDataOpt, mediaMatch) => {
    const browsers = PlatformInfo.browsers();
    const oses = PlatformInfo.oses();
    const browser = userAgentDataOpt.bind(userAgentData => detectBrowser$1(browsers, userAgentData)).orThunk(() => detectBrowser(browsers, userAgent)).fold(Browser.unknown, Browser.nu);
    const os = detectOs(oses, userAgent).fold(OperatingSystem.unknown, OperatingSystem.nu);
    const deviceType = DeviceType(os, browser, userAgent, mediaMatch);
    return {
      browser,
      os,
      deviceType
    };
  };
  const PlatformDetection = { detect: detect$3 };

  const mediaMatch = query => window.matchMedia(query).matches;
  let platform = cached(() => PlatformDetection.detect(window.navigator.userAgent, Optional.from(window.navigator.userAgentData), mediaMatch));
  const detect$2 = () => platform();

  const Dimension = (name, getOffset) => {
    const set = (element, h) => {
      if (!isNumber(h) && !h.match(/^[0-9]+$/)) {
        throw new Error(name + ".set accepts only positive integer values. Value was " + h);
      }
      const dom = element.dom;
      if (isSupported(dom)) {
        dom.style[name] = h + "px";
      }
    };
    const get = element => {
      const r = getOffset(element);
      if (r <= 0 || r === null) {
        const css = get$a(element, name);
        return parseFloat(css) || 0;
      }
      return r;
    };
    const getOuter = get;
    const aggregate = (element, properties) => foldl(properties, (acc, property) => {
      const val = get$a(element, property);
      const value = val === undefined ? 0 : parseInt(val, 10);
      return isNaN(value) ? acc : acc + value;
    }, 0);
    const max = (element, value, properties) => {
      const cumulativeInclusions = aggregate(element, properties);
      const absoluteMax = value > cumulativeInclusions ? value - cumulativeInclusions : 0;
      return absoluteMax;
    };
    return {
      set,
      get,
      getOuter,
      aggregate,
      max
    };
  };

  const toNumber = (px, fallback) => toFloat(px).getOr(fallback);
  const getProp = (element, name, fallback) => toNumber(get$a(element, name), fallback);
  const calcContentBoxSize = (element, size, upper, lower) => {
    const paddingUpper = getProp(element, `padding-${upper}`, 0);
    const paddingLower = getProp(element, `padding-${lower}`, 0);
    const borderUpper = getProp(element, `border-${upper}-width`, 0);
    const borderLower = getProp(element, `border-${lower}-width`, 0);
    return size - paddingUpper - paddingLower - borderUpper - borderLower;
  };
  const getCalculatedWidth = (element, boxSizing) => {
    const dom = element.dom;
    const width = dom.getBoundingClientRect().width || dom.offsetWidth;
    return boxSizing === "border-box" ? width : calcContentBoxSize(element, width, "left", "right");
  };
  const getHeight$1 = element => getProp(element, "height", element.dom.offsetHeight);
  const getWidth = element => getProp(element, "width", element.dom.offsetWidth);
  const getInnerWidth = element => getCalculatedWidth(element, "content-box");

  const api$2 = Dimension("width", element => element.dom.offsetWidth);
  const get$9 = element => api$2.get(element);
  const getOuter$2 = element => api$2.getOuter(element);
  const getInner = getInnerWidth;
  const getRuntime$1 = getWidth;

  const addCells = (gridRow, index, cells) => {
    const existingCells = gridRow.cells;
    const before = existingCells.slice(0, index);
    const after = existingCells.slice(index);
    const newCells = before.concat(cells).concat(after);
    return setCells(gridRow, newCells);
  };
  const addCell = (gridRow, index, cell) => addCells(gridRow, index, [cell]);
  const mutateCell = (gridRow, index, cell) => {
    const cells = gridRow.cells;
    cells[index] = cell;
  };
  const setCells = (gridRow, cells) => rowcells(gridRow.element, cells, gridRow.section, gridRow.isNew);
  const mapCells = (gridRow, f) => {
    const cells = gridRow.cells;
    const r = map$1(cells, f);
    return rowcells(gridRow.element, r, gridRow.section, gridRow.isNew);
  };
  const getCell = (gridRow, index) => gridRow.cells[index];
  const getCellElement = (gridRow, index) => getCell(gridRow, index).element;
  const cellLength = gridRow => gridRow.cells.length;
  const extractGridDetails = grid => {
    const result = partition(grid, row => row.section === "colgroup");
    return {
      rows: result.fail,
      cols: result.pass
    };
  };
  const clone = (gridRow, cloneRow, cloneCell) => {
    const newCells = map$1(gridRow.cells, cloneCell);
    return rowcells(cloneRow(gridRow.element), newCells, gridRow.section, true);
  };

  const LOCKED_COL_ATTR = "data-snooker-locked-cols";
  const getLockedColumnsFromTable = table => getOpt(table, LOCKED_COL_ATTR).bind(lockedColStr => Optional.from(lockedColStr.match(/\d+/g))).map(lockedCols => mapToObject(lockedCols, always));
  const getLockedColumnsFromGrid = grid => {
    const locked = foldl(extractGridDetails(grid).rows, (acc, row) => {
      each$2(row.cells, (cell, idx) => {
        if (cell.isLocked) {
          acc[idx] = true;
        }
      });
      return acc;
    }, {});
    const lockedArr = mapToArray(locked, (_val, key) => parseInt(key, 10));
    return sort$1(lockedArr);
  };

  const key = (row, column) => {
    return row + "," + column;
  };
  const getAt = (warehouse, row, column) => Optional.from(warehouse.access[key(row, column)]);
  const findItem = (warehouse, item, comparator) => {
    const filtered = filterItems(warehouse, detail => {
      return comparator(item, detail.element);
    });
    return filtered.length > 0 ? Optional.some(filtered[0]) : Optional.none();
  };
  const filterItems = (warehouse, predicate) => {
    const all = bind$2(warehouse.all, r => {
      return r.cells;
    });
    return filter$2(all, predicate);
  };
  const generateColumns = rowData => {
    const columnsGroup = {};
    let index = 0;
    each$2(rowData.cells, column => {
      const colspan = column.colspan;
      range$1(colspan, columnIndex => {
        const colIndex = index + columnIndex;
        columnsGroup[colIndex] = columnext(column.element, colspan, colIndex);
      });
      index += colspan;
    });
    return columnsGroup;
  };
  const generate$2 = list => {
    const access = {};
    const cells = [];
    const tableOpt = head(list).map(rowData => rowData.element).bind(table);
    const lockedColumns = tableOpt.bind(getLockedColumnsFromTable).getOr({});
    let maxRows = 0;
    let maxColumns = 0;
    let rowCount = 0;
    const {
      pass: colgroupRows,
      fail: rows
    } = partition(list, rowData => rowData.section === "colgroup");
    each$2(rows, rowData => {
      const currentRow = [];
      each$2(rowData.cells, rowCell => {
        let start = 0;
        while (access[key(rowCount, start)] !== undefined) {
          start++;
        }
        const isLocked = hasNonNullableKey(lockedColumns, start.toString());
        const current = extended(rowCell.element, rowCell.rowspan, rowCell.colspan, rowCount, start, isLocked);
        for (let occupiedColumnPosition = 0; occupiedColumnPosition < rowCell.colspan; occupiedColumnPosition++) {
          for (let occupiedRowPosition = 0; occupiedRowPosition < rowCell.rowspan; occupiedRowPosition++) {
            const rowPosition = rowCount + occupiedRowPosition;
            const columnPosition = start + occupiedColumnPosition;
            const newpos = key(rowPosition, columnPosition);
            access[newpos] = current;
            maxColumns = Math.max(maxColumns, columnPosition + 1);
          }
        }
        currentRow.push(current);
      });
      maxRows++;
      cells.push(rowdetail(rowData.element, currentRow, rowData.section));
      rowCount++;
    });
    const { columns, colgroups } = last$2(colgroupRows).map(rowData => {
      const columns = generateColumns(rowData);
      const colgroup$1 = colgroup(rowData.element, values(columns));
      return {
        colgroups: [colgroup$1],
        columns
      };
    }).getOrThunk(() => ({
      colgroups: [],
      columns: {}
    }));
    const grid$1 = grid(maxRows, maxColumns);
    return {
      grid: grid$1,
      access,
      all: cells,
      columns,
      colgroups
    };
  };
  const fromTable = table => {
    const list = fromTable$1(table);
    return generate$2(list);
  };
  const justCells = warehouse => bind$2(warehouse.all, w => w.cells);
  const justColumns = warehouse => values(warehouse.columns);
  const hasColumns = warehouse => keys(warehouse.columns).length > 0;
  const getColumnAt = (warehouse, columnIndex) => Optional.from(warehouse.columns[columnIndex]);
  const Warehouse = {
    fromTable,
    generate: generate$2,
    getAt,
    findItem,
    filterItems,
    justCells,
    justColumns,
    hasColumns,
    getColumnAt
  };

  const columns = (warehouse, isValidCell = always) => {
    const grid = warehouse.grid;
    const cols = range$1(grid.columns, identity);
    const rowsArr = range$1(grid.rows, identity);
    return map$1(cols, col => {
      const getBlock = () => bind$2(rowsArr, r => Warehouse.getAt(warehouse, r, col).filter(detail => detail.column === col).toArray());
      const isValid = detail => detail.colspan === 1 && isValidCell(detail.element);
      const getFallback = () => Warehouse.getAt(warehouse, 0, col);
      return decide(getBlock, isValid, getFallback);
    });
  };
  const decide = (getBlock, isValid, getFallback) => {
    const inBlock = getBlock();
    const validInBlock = find$1(inBlock, isValid);
    const detailOption = validInBlock.orThunk(() => Optional.from(inBlock[0]).orThunk(getFallback));
    return detailOption.map(detail => detail.element);
  };
  const rows = warehouse => {
    const grid = warehouse.grid;
    const rowsArr = range$1(grid.rows, identity);
    const cols = range$1(grid.columns, identity);
    return map$1(rowsArr, row => {
      const getBlock = () => bind$2(cols, c => Warehouse.getAt(warehouse, row, c).filter(detail => detail.row === row).fold(constant([]), detail => [detail]));
      const isSingle = detail => detail.rowspan === 1;
      const getFallback = () => Warehouse.getAt(warehouse, row, 0);
      return decide(getBlock, isSingle, getFallback);
    });
  };

  const deduce = (xs, index) => {
    if (index < 0 || index >= xs.length - 1) {
      return Optional.none();
    }
    const current = xs[index].fold(() => {
      const rest = reverse(xs.slice(0, index));
      return findMap(rest, (a, i) => a.map(aa => ({
        value: aa,
        delta: i + 1
      })));
    }, c => Optional.some({
      value: c,
      delta: 0
    }));
    const next = xs[index + 1].fold(() => {
      const rest = xs.slice(index + 1);
      return findMap(rest, (a, i) => a.map(aa => ({
        value: aa,
        delta: i + 1
      })));
    }, n => Optional.some({
      value: n,
      delta: 1
    }));
    return current.bind(c => next.map(n => {
      const extras = n.delta + c.delta;
      return Math.abs(n.value - c.value) / extras;
    }));
  };

  const onDirection = (isLtr, isRtl) => element => getDirection(element) === "rtl" ? isRtl : isLtr;
  const getDirection = element => get$a(element, "direction") === "rtl" ? "rtl" : "ltr";

  const api$1 = Dimension("height", element => {
    const dom = element.dom;
    return inBody(element) ? dom.getBoundingClientRect().height : dom.offsetHeight;
  });
  const get$8 = element => api$1.get(element);
  const getOuter$1 = element => api$1.getOuter(element);
  const getRuntime = getHeight$1;

  const r = (left, top) => {
    const translate = (x, y) => r(left + x, top + y);
    return {
      left,
      top,
      translate
    };
  };
  const SugarPosition = r;

  const boxPosition = dom => {
    const box = dom.getBoundingClientRect();
    return SugarPosition(box.left, box.top);
  };
  const firstDefinedOrZero = (a, b) => {
    if (a !== undefined) {
      return a;
    } else {
      return b !== undefined ? b : 0;
    }
  };
  const absolute = element => {
    const doc = element.dom.ownerDocument;
    const body = doc.body;
    const win = doc.defaultView;
    const html = doc.documentElement;
    if (body === element.dom) {
      return SugarPosition(body.offsetLeft, body.offsetTop);
    }
    const scrollTop = firstDefinedOrZero(win === null || win === void 0 ? void 0 : win.pageYOffset, html.scrollTop);
    const scrollLeft = firstDefinedOrZero(win === null || win === void 0 ? void 0 : win.pageXOffset, html.scrollLeft);
    const clientTop = firstDefinedOrZero(html.clientTop, body.clientTop);
    const clientLeft = firstDefinedOrZero(html.clientLeft, body.clientLeft);
    return viewport(element).translate(scrollLeft - clientLeft, scrollTop - clientTop);
  };
  const viewport = element => {
    const dom = element.dom;
    const doc = dom.ownerDocument;
    const body = doc.body;
    if (body === dom) {
      return SugarPosition(body.offsetLeft, body.offsetTop);
    }
    if (!inBody(element)) {
      return SugarPosition(0, 0);
    }
    return boxPosition(dom);
  };

  const rowInfo = (row, y) => ({
    row,
    y
  });
  const colInfo = (col, x) => ({
    col,
    x
  });
  const rtlEdge = cell => {
    const pos = absolute(cell);
    return pos.left + getOuter$2(cell);
  };
  const ltrEdge = cell => {
    return absolute(cell).left;
  };
  const getLeftEdge = (index, cell) => {
    return colInfo(index, ltrEdge(cell));
  };
  const getRightEdge = (index, cell) => {
    return colInfo(index, rtlEdge(cell));
  };
  const getTop$1 = cell => {
    return absolute(cell).top;
  };
  const getTopEdge = (index, cell) => {
    return rowInfo(index, getTop$1(cell));
  };
  const getBottomEdge = (index, cell) => {
    return rowInfo(index, getTop$1(cell) + getOuter$1(cell));
  };
  const findPositions = (getInnerEdge, getOuterEdge, array) => {
    if (array.length === 0) {
      return [];
    }
    const lines = map$1(array.slice(1), (cellOption, index) => {
      return cellOption.map(cell => {
        return getInnerEdge(index, cell);
      });
    });
    const lastLine = array[array.length - 1].map(cell => {
      return getOuterEdge(array.length - 1, cell);
    });
    return lines.concat([lastLine]);
  };
  const negate = step => {
    return -step;
  };
  const height = {
    delta: identity,
    positions: optElements => findPositions(getTopEdge, getBottomEdge, optElements),
    edge: getTop$1
  };
  const ltr$1 = {
    delta: identity,
    edge: ltrEdge,
    positions: optElements => findPositions(getLeftEdge, getRightEdge, optElements)
  };
  const rtl$1 = {
    delta: negate,
    edge: rtlEdge,
    positions: optElements => findPositions(getRightEdge, getLeftEdge, optElements)
  };
  const detect$1 = onDirection(ltr$1, rtl$1);
  const width = {
    delta: (amount, table) => detect$1(table).delta(amount, table),
    positions: (cols, table) => detect$1(table).positions(cols, table),
    edge: cell => detect$1(cell).edge(cell)
  };

  const units = {
    unsupportedLength: [
      "em",
      "ex",
      "cap",
      "ch",
      "ic",
      "rem",
      "lh",
      "rlh",
      "vw",
      "vh",
      "vi",
      "vb",
      "vmin",
      "vmax",
      "cm",
      "mm",
      "Q",
      "in",
      "pc",
      "pt",
      "px"
    ],
    fixed: [
      "px",
      "pt"
    ],
    relative: ["%"],
    empty: [""]
  };
  const pattern = (() => {
    const decimalDigits = "[0-9]+";
    const signedInteger = "[+-]?" + decimalDigits;
    const exponentPart = "[eE]" + signedInteger;
    const dot = "\\.";
    const opt = input => `(?:${input})?`;
    const unsignedDecimalLiteral = [
      "Infinity",
      decimalDigits + dot + opt(decimalDigits) + opt(exponentPart),
      dot + decimalDigits + opt(exponentPart),
      decimalDigits + opt(exponentPart)
    ].join("|");
    const float = `[+-]?(?:${unsignedDecimalLiteral})`;
    return new RegExp(`^(${float})(.*)$`);
  })();
  const isUnit = (unit, accepted) => exists(accepted, acc => exists(units[acc], check => unit === check));
  const parse = (input, accepted) => {
    const match = Optional.from(pattern.exec(input));
    return match.bind(array => {
      const value = Number(array[1]);
      const unitRaw = array[2];
      if (isUnit(unitRaw, accepted)) {
        return Optional.some({
          value,
          unit: unitRaw
        });
      } else {
        return Optional.none();
      }
    });
  };

  const rPercentageBasedSizeRegex = /(\d+(\.\d+)?)%/;
  const rPixelBasedSizeRegex = /(\d+(\.\d+)?)px|em/;
  const isCol$2 = isTag("col");
  const isRow$2 = isTag("tr");
  const getPercentSize = (elm, outerGetter, innerGetter) => {
    const relativeParent = parentElement(elm).getOrThunk(() => getBody$1(owner(elm)));
    return outerGetter(elm) / innerGetter(relativeParent) * 100;
  };
  const setPixelWidth = (cell, amount) => {
    set$1(cell, "width", amount + "px");
  };
  const setPercentageWidth = (cell, amount) => {
    set$1(cell, "width", amount + "%");
  };
  const setHeight = (cell, amount) => {
    set$1(cell, "height", amount + "px");
  };
  const removeHeight = cell => {
    remove$5(cell, "height");
  };
  const getHeightValue = cell => getRuntime(cell) + "px";
  const convert = (cell, number, getter, setter) => {
    const newSize = table(cell).map(table => {
      const total = getter(table);
      return Math.floor(number / 100 * total);
    }).getOr(number);
    setter(cell, newSize);
    return newSize;
  };
  const normalizePixelSize = (value, cell, getter, setter) => {
    const number = parseFloat(value);
    return endsWith(value, "%") && name(cell) !== "table" ? convert(cell, number, getter, setter) : number;
  };
  const getTotalHeight = cell => {
    const value = getHeightValue(cell);
    if (!value) {
      return get$8(cell);
    }
    return normalizePixelSize(value, cell, get$8, setHeight);
  };
  const get$7 = (cell, type, f) => {
    const v = f(cell);
    const span = getSpan(cell, type);
    return v / span;
  };
  const getRaw$1 = (element, prop) => {
    return getRaw$2(element, prop).orThunk(() => {
      return getOpt(element, prop).map(val => val + "px");
    });
  };
  const getRawWidth$1 = element => getRaw$1(element, "width");
  const getRawHeight$1 = element => getRaw$1(element, "height");
  const getPercentageWidth = cell => getPercentSize(cell, get$9, getInner);
  const getPixelWidth$1 = cell => isCol$2(cell) ? get$9(cell) : getRuntime$1(cell);
  const getHeight = cell => {
    return isRow$2(cell) ? get$8(cell) : get$7(cell, "rowspan", getTotalHeight);
  };
  const getGenericWidth = cell => {
    const width = getRawWidth$1(cell);
    return width.bind(w => parse(w, [
      "fixed",
      "relative",
      "empty"
    ]));
  };
  const setGenericWidth = (cell, amount, unit) => {
    set$1(cell, "width", amount + unit);
  };
  const getPixelTableWidth = table => get$9(table) + "px";
  const getPixelTableHeight = table => get$8(table) + "px";
  const getPercentTableWidth = table => getPercentSize(table, get$9, getInner) + "%";
  const isPercentSizing$1 = table => getRawWidth$1(table).exists(size => rPercentageBasedSizeRegex.test(size));
  const isPixelSizing$1 = table => getRawWidth$1(table).exists(size => rPixelBasedSizeRegex.test(size));
  const isNoneSizing$1 = table => getRawWidth$1(table).isNone();
  const percentageBasedSizeRegex = constant(rPercentageBasedSizeRegex);

  const isCol$1 = isTag("col");
  const getRawW = cell => {
    return getRawWidth$1(cell).getOrThunk(() => getPixelWidth$1(cell) + "px");
  };
  const getRawH = cell => {
    return getRawHeight$1(cell).getOrThunk(() => getHeight(cell) + "px");
  };
  const justCols = warehouse => map$1(Warehouse.justColumns(warehouse), column => Optional.from(column.element));
  const isValidColumn = cell => {
    const browser = detect$2().browser;
    const supportsColWidths = browser.isChromium() || browser.isFirefox();
    return isCol$1(cell) ? supportsColWidths : true;
  };
  const getDimension = (cellOpt, index, backups, filter, getter, fallback) => cellOpt.filter(filter).fold(() => fallback(deduce(backups, index)), cell => getter(cell));
  const getWidthFrom = (warehouse, table, getWidth, fallback) => {
    const columnCells = columns(warehouse);
    const columns$1 = Warehouse.hasColumns(warehouse) ? justCols(warehouse) : columnCells;
    const backups = [Optional.some(width.edge(table))].concat(map$1(width.positions(columnCells, table), pos => pos.map(p => p.x)));
    const colFilter = not(hasColspan);
    return map$1(columns$1, (cellOption, c) => {
      return getDimension(cellOption, c, backups, colFilter, column => {
        if (isValidColumn(column)) {
          return getWidth(column);
        } else {
          const cell = bindFrom(columnCells[c], identity);
          return getDimension(cell, c, backups, colFilter, cell => fallback(Optional.some(get$9(cell))), fallback);
        }
      }, fallback);
    });
  };
  const getDeduced = deduced => {
    return deduced.map(d => {
      return d + "px";
    }).getOr("");
  };
  const getRawWidths = (warehouse, table) => {
    return getWidthFrom(warehouse, table, getRawW, getDeduced);
  };
  const getPercentageWidths = (warehouse, table, tableSize) => {
    return getWidthFrom(warehouse, table, getPercentageWidth, deduced => {
      return deduced.fold(() => {
        return tableSize.minCellWidth();
      }, cellWidth => {
        return cellWidth / tableSize.pixelWidth() * 100;
      });
    });
  };
  const getPixelWidths = (warehouse, table, tableSize) => {
    return getWidthFrom(warehouse, table, getPixelWidth$1, deduced => {
      return deduced.getOrThunk(tableSize.minCellWidth);
    });
  };
  const getHeightFrom = (warehouse, table, getHeight, fallback) => {
    const rowCells = rows(warehouse);
    const rows$1 = map$1(warehouse.all, r => Optional.some(r.element));
    const backups = [Optional.some(height.edge(table))].concat(map$1(height.positions(rowCells, table), pos => pos.map(p => p.y)));
    return map$1(rows$1, (row, i) => getDimension(row, i, backups, always, getHeight, fallback));
  };
  const getPixelHeights = (warehouse, table) => {
    return getHeightFrom(warehouse, table, getHeight, deduced => {
      return deduced.getOrThunk(minHeight);
    });
  };
  const getRawHeights = (warehouse, table) => {
    return getHeightFrom(warehouse, table, getRawH, getDeduced);
  };

  const widthLookup = (table, getter) => () => {
    if (inBody(table)) {
      return getter(table);
    } else {
      return parseFloat(getRaw$2(table, "width").getOr("0"));
    }
  };
  const noneSize = table => {
    const getWidth = widthLookup(table, get$9);
    const zero = constant(0);
    const getWidths = (warehouse, tableSize) => getPixelWidths(warehouse, table, tableSize);
    return {
      width: getWidth,
      pixelWidth: getWidth,
      getWidths,
      getCellDelta: zero,
      singleColumnWidth: constant([0]),
      minCellWidth: zero,
      setElementWidth: noop,
      adjustTableWidth: noop,
      isRelative: true,
      label: "none"
    };
  };
  const percentageSize = table => {
    const getFloatWidth = widthLookup(table, elem => parseFloat(getPercentTableWidth(elem)));
    const getWidth = widthLookup(table, get$9);
    const getCellDelta = delta => delta / getWidth() * 100;
    const singleColumnWidth = (w, _delta) => [100 - w];
    const minCellWidth = () => minWidth() / getWidth() * 100;
    const adjustTableWidth = delta => {
      const currentWidth = getFloatWidth();
      const change = delta / 100 * currentWidth;
      const newWidth = currentWidth + change;
      setPercentageWidth(table, newWidth);
    };
    const getWidths = (warehouse, tableSize) => getPercentageWidths(warehouse, table, tableSize);
    return {
      width: getFloatWidth,
      pixelWidth: getWidth,
      getWidths,
      getCellDelta,
      singleColumnWidth,
      minCellWidth,
      setElementWidth: setPercentageWidth,
      adjustTableWidth,
      isRelative: true,
      label: "percent"
    };
  };
  const pixelSize = table => {
    const getWidth = widthLookup(table, get$9);
    const getCellDelta = identity;
    const singleColumnWidth = (w, delta) => {
      const newNext = Math.max(minWidth(), w + delta);
      return [newNext - w];
    };
    const adjustTableWidth = delta => {
      const newWidth = getWidth() + delta;
      setPixelWidth(table, newWidth);
    };
    const getWidths = (warehouse, tableSize) => getPixelWidths(warehouse, table, tableSize);
    return {
      width: getWidth,
      pixelWidth: getWidth,
      getWidths,
      getCellDelta,
      singleColumnWidth,
      minCellWidth: minWidth,
      setElementWidth: setPixelWidth,
      adjustTableWidth,
      isRelative: false,
      label: "pixel"
    };
  };
  const chooseSize = (element, width) => {
    const percentMatch = percentageBasedSizeRegex().exec(width);
    if (percentMatch !== null) {
      return percentageSize(element);
    } else {
      return pixelSize(element);
    }
  };
  const getTableSize = table => {
    const width = getRawWidth$1(table);
    return width.fold(() => noneSize(table), w => chooseSize(table, w));
  };
  const TableSize = {
    getTableSize,
    pixelSize,
    percentageSize,
    noneSize
  };

  const statsStruct = (minRow, minCol, maxRow, maxCol, allCells, selectedCells) => ({
    minRow,
    minCol,
    maxRow,
    maxCol,
    allCells,
    selectedCells
  });
  const findSelectedStats = (house, isSelected) => {
    const totalColumns = house.grid.columns;
    const totalRows = house.grid.rows;
    let minRow = totalRows;
    let minCol = totalColumns;
    let maxRow = 0;
    let maxCol = 0;
    const allCells = [];
    const selectedCells = [];
    each$1(house.access, detail => {
      allCells.push(detail);
      if (isSelected(detail)) {
        selectedCells.push(detail);
        const startRow = detail.row;
        const endRow = startRow + detail.rowspan - 1;
        const startCol = detail.column;
        const endCol = startCol + detail.colspan - 1;
        if (startRow < minRow) {
          minRow = startRow;
        } else if (endRow > maxRow) {
          maxRow = endRow;
        }
        if (startCol < minCol) {
          minCol = startCol;
        } else if (endCol > maxCol) {
          maxCol = endCol;
        }
      }
    });
    return statsStruct(minRow, minCol, maxRow, maxCol, allCells, selectedCells);
  };
  const makeCell = (list, seenSelected, rowIndex) => {
    const row = list[rowIndex].element;
    const td = SugarElement.fromTag("td");
    append$1(td, SugarElement.fromTag("br"));
    const f = seenSelected ? append$1 : prepend;
    f(row, td);
  };
  const fillInGaps = (list, house, stats, isSelected) => {
    const rows = filter$2(list, row => row.section !== "colgroup");
    const totalColumns = house.grid.columns;
    const totalRows = house.grid.rows;
    for (let i = 0; i < totalRows; i++) {
      let seenSelected = false;
      for (let j = 0; j < totalColumns; j++) {
        if (!(i < stats.minRow || i > stats.maxRow || j < stats.minCol || j > stats.maxCol)) {
          const needCell = Warehouse.getAt(house, i, j).filter(isSelected).isNone();
          if (needCell) {
            makeCell(rows, seenSelected, i);
          } else {
            seenSelected = true;
          }
        }
      }
    }
  };
  const clean = (replica, stats, house, widthDelta) => {
    each$1(house.columns, col => {
      if (col.column < stats.minCol || col.column > stats.maxCol) {
        remove$6(col.element);
      }
    });
    const emptyRows = filter$2(firstLayer(replica, "tr"), row => row.dom.childElementCount === 0);
    each$2(emptyRows, remove$6);
    if (stats.minCol === stats.maxCol || stats.minRow === stats.maxRow) {
      each$2(firstLayer(replica, "th,td"), cell => {
        remove$7(cell, "rowspan");
        remove$7(cell, "colspan");
      });
    }
    remove$7(replica, LOCKED_COL_ATTR);
    remove$7(replica, "data-snooker-col-series");
    const tableSize = TableSize.getTableSize(replica);
    tableSize.adjustTableWidth(widthDelta);
  };
  const getTableWidthDelta = (table, warehouse, tableSize, stats) => {
    if (stats.minCol === 0 && warehouse.grid.columns === stats.maxCol + 1) {
      return 0;
    }
    const colWidths = getPixelWidths(warehouse, table, tableSize);
    const allColsWidth = foldl(colWidths, (acc, width) => acc + width, 0);
    const selectedColsWidth = foldl(colWidths.slice(stats.minCol, stats.maxCol + 1), (acc, width) => acc + width, 0);
    const newWidth = selectedColsWidth / allColsWidth * tableSize.pixelWidth();
    const delta = newWidth - tableSize.pixelWidth();
    return tableSize.getCellDelta(delta);
  };
  const extract$1 = (table, selectedSelector) => {
    const isSelected = detail => is$2(detail.element, selectedSelector);
    const replica = deep(table);
    const list = fromTable$1(replica);
    const tableSize = TableSize.getTableSize(table);
    const replicaHouse = Warehouse.generate(list);
    const replicaStats = findSelectedStats(replicaHouse, isSelected);
    const selector = "th:not(" + selectedSelector + ")" + ",td:not(" + selectedSelector + ")";
    const unselectedCells = filterFirstLayer(replica, "th,td", cell => is$2(cell, selector));
    each$2(unselectedCells, remove$6);
    fillInGaps(list, replicaHouse, replicaStats, isSelected);
    const house = Warehouse.fromTable(table);
    const widthDelta = getTableWidthDelta(table, house, tableSize, replicaStats);
    clean(replica, replicaStats, replicaHouse, widthDelta);
    return replica;
  };

  const nbsp = "\xA0";

  const NodeValue = (is, name) => {
    const get = element => {
      if (!is(element)) {
        throw new Error("Can only get " + name + " value of a " + name + " node");
      }
      return getOption(element).getOr("");
    };
    const getOption = element => is(element) ? Optional.from(element.dom.nodeValue) : Optional.none();
    const set = (element, value) => {
      if (!is(element)) {
        throw new Error("Can only set raw " + name + " value of a " + name + " node");
      }
      element.dom.nodeValue = value;
    };
    return {
      get,
      getOption,
      set
    };
  };

  const api = NodeValue(isText, "text");
  const get$6 = element => api.get(element);
  const getOption = element => api.getOption(element);
  const set = (element, value) => api.set(element, value);

  const getEnd = element => name(element) === "img" ? 1 : getOption(element).fold(() => children$2(element).length, v => v.length);
  const isTextNodeWithCursorPosition = el => getOption(el).filter(text => text.trim().length !== 0 || text.indexOf(nbsp) > -1).isSome();
  const isContentEditableFalse = elem => isHTMLElement(elem) && get$b(elem, "contenteditable") === "false";
  const elementsWithCursorPosition = [
    "img",
    "br"
  ];
  const isCursorPosition = elem => {
    const hasCursorPosition = isTextNodeWithCursorPosition(elem);
    return hasCursorPosition || contains$2(elementsWithCursorPosition, name(elem)) || isContentEditableFalse(elem);
  };

  const first = element => descendant$1(element, isCursorPosition);
  const last$1 = element => descendantRtl(element, isCursorPosition);
  const descendantRtl = (scope, predicate) => {
    const descend = element => {
      const children = children$2(element);
      for (let i = children.length - 1; i >= 0; i--) {
        const child = children[i];
        if (predicate(child)) {
          return Optional.some(child);
        }
        const res = descend(child);
        if (res.isSome()) {
          return res;
        }
      }
      return Optional.none();
    };
    return descend(scope);
  };

  const transferableAttributes = {
    scope: [
      "row",
      "col"
    ]
  };
  const createCell = doc => () => {
    const td = SugarElement.fromTag("td", doc.dom);
    append$1(td, SugarElement.fromTag("br", doc.dom));
    return td;
  };
  const createCol = doc => () => {
    return SugarElement.fromTag("col", doc.dom);
  };
  const createColgroup = doc => () => {
    return SugarElement.fromTag("colgroup", doc.dom);
  };
  const createRow$1 = doc => () => {
    return SugarElement.fromTag("tr", doc.dom);
  };
  const replace$1 = (cell, tag, attrs) => {
    const replica = copy$2(cell, tag);
    each$1(attrs, (v, k) => {
      if (v === null) {
        remove$7(replica, k);
      } else {
        set$2(replica, k, v);
      }
    });
    return replica;
  };
  const pasteReplace = cell => {
    return cell;
  };
  const cloneFormats = (oldCell, newCell, formats) => {
    const first$1 = first(oldCell);
    return first$1.map(firstText => {
      const formatSelector = formats.join(",");
      const parents = ancestors$3(firstText, formatSelector, element => {
        return eq$1(element, oldCell);
      });
      return foldr(parents, (last, parent) => {
        const clonedFormat = shallow(parent);
        append$1(last, clonedFormat);
        return clonedFormat;
      }, newCell);
    }).getOr(newCell);
  };
  const cloneAppropriateAttributes = (original, clone) => {
    each$1(transferableAttributes, (validAttributes, attributeName) => getOpt(original, attributeName).filter(attribute => contains$2(validAttributes, attribute)).each(attribute => set$2(clone, attributeName, attribute)));
  };
  const cellOperations = (mutate, doc, formatsToClone) => {
    const cloneCss = (prev, clone) => {
      copy$1(prev.element, clone);
      remove$5(clone, "height");
      if (prev.colspan !== 1) {
        remove$5(clone, "width");
      }
    };
    const newCell = prev => {
      const td = SugarElement.fromTag(name(prev.element), doc.dom);
      const formats = formatsToClone.getOr([
        "strong",
        "em",
        "b",
        "i",
        "span",
        "font",
        "h1",
        "h2",
        "h3",
        "h4",
        "h5",
        "h6",
        "p",
        "div"
      ]);
      const lastNode = formats.length > 0 ? cloneFormats(prev.element, td, formats) : td;
      append$1(lastNode, SugarElement.fromTag("br"));
      cloneCss(prev, td);
      cloneAppropriateAttributes(prev.element, td);
      mutate(prev.element, td);
      return td;
    };
    const newCol = prev => {
      const col = SugarElement.fromTag(name(prev.element), doc.dom);
      cloneCss(prev, col);
      mutate(prev.element, col);
      return col;
    };
    return {
      col: newCol,
      colgroup: createColgroup(doc),
      row: createRow$1(doc),
      cell: newCell,
      replace: replace$1,
      colGap: createCol(doc),
      gap: createCell(doc)
    };
  };
  const paste$1 = doc => {
    return {
      col: createCol(doc),
      colgroup: createColgroup(doc),
      row: createRow$1(doc),
      cell: createCell(doc),
      replace: pasteReplace,
      colGap: createCol(doc),
      gap: createCell(doc)
    };
  };

  const fromHtml = (html, scope) => {
    const doc = scope || document;
    const div = doc.createElement("div");
    div.innerHTML = html;
    return children$2(SugarElement.fromDom(div));
  };
  const fromDom = nodes => map$1(nodes, SugarElement.fromDom);

  const option = name => editor => editor.options.get(name);
  const defaultWidth = "100%";
  const getPixelForcedWidth = editor => {
    var _a;
    const dom = editor.dom;
    const parentBlock = (_a = dom.getParent(editor.selection.getStart(), dom.isBlock)) !== null && _a !== void 0 ? _a : editor.getBody();
    return getInner(SugarElement.fromDom(parentBlock)) + "px";
  };
  const determineDefaultTableStyles = (editor, defaultStyles) => {
    if (isTableResponsiveForced(editor) || !shouldStyleWithCss(editor)) {
      return defaultStyles;
    } else if (isTablePixelsForced(editor)) {
      return {
        ...defaultStyles,
        width: getPixelForcedWidth(editor)
      };
    } else {
      return {
        ...defaultStyles,
        width: defaultWidth
      };
    }
  };
  const determineDefaultTableAttributes = (editor, defaultAttributes) => {
    if (isTableResponsiveForced(editor) || shouldStyleWithCss(editor)) {
      return defaultAttributes;
    } else if (isTablePixelsForced(editor)) {
      return {
        ...defaultAttributes,
        width: getPixelForcedWidth(editor)
      };
    } else {
      return {
        ...defaultAttributes,
        width: defaultWidth
      };
    }
  };
  const register = editor => {
    const registerOption = editor.options.register;
    registerOption("table_clone_elements", { processor: "string[]" });
    registerOption("table_use_colgroups", {
      processor: "boolean",
      default: true
    });
    registerOption("table_header_type", {
      processor: value => {
        const valid = contains$2([
          "section",
          "cells",
          "sectionCells",
          "auto"
        ], value);
        return valid ? {
          value,
          valid
        } : {
          valid: false,
          message: "Must be one of: section, cells, sectionCells or auto."
        };
      },
      default: "section"
    });
    registerOption("table_sizing_mode", {
      processor: "string",
      default: "auto"
    });
    registerOption("table_default_attributes", {
      processor: "object",
      default: { border: "1" }
    });
    registerOption("table_default_styles", {
      processor: "object",
      default: { "border-collapse": "collapse" }
    });
    registerOption("table_column_resizing", {
      processor: value => {
        const valid = contains$2([
          "preservetable",
          "resizetable"
        ], value);
        return valid ? {
          value,
          valid
        } : {
          valid: false,
          message: "Must be preservetable, or resizetable."
        };
      },
      default: "preservetable"
    });
    registerOption("table_resize_bars", {
      processor: "boolean",
      default: true
    });
    registerOption("table_style_by_css", {
      processor: "boolean",
      default: true
    });
    registerOption("table_merge_content_on_paste", {
      processor: "boolean",
      default: true
    });
  };
  const getTableCloneElements = editor => {
    return Optional.from(editor.options.get("table_clone_elements"));
  };
  const hasTableObjectResizing = editor => {
    const objectResizing = editor.options.get("object_resizing");
    return contains$2(objectResizing.split(","), "table");
  };
  const getTableHeaderType = option("table_header_type");
  const getTableColumnResizingBehaviour = option("table_column_resizing");
  const isPreserveTableColumnResizing = editor => getTableColumnResizingBehaviour(editor) === "preservetable";
  const isResizeTableColumnResizing = editor => getTableColumnResizingBehaviour(editor) === "resizetable";
  const getTableSizingMode = option("table_sizing_mode");
  const isTablePercentagesForced = editor => getTableSizingMode(editor) === "relative";
  const isTablePixelsForced = editor => getTableSizingMode(editor) === "fixed";
  const isTableResponsiveForced = editor => getTableSizingMode(editor) === "responsive";
  const hasTableResizeBars = option("table_resize_bars");
  const shouldStyleWithCss = option("table_style_by_css");
  const shouldMergeContentOnPaste = option("table_merge_content_on_paste");
  const getTableDefaultAttributes = editor => {
    const options = editor.options;
    const defaultAttributes = options.get("table_default_attributes");
    return options.isSet("table_default_attributes") ? defaultAttributes : determineDefaultTableAttributes(editor, defaultAttributes);
  };
  const getTableDefaultStyles = editor => {
    const options = editor.options;
    const defaultStyles = options.get("table_default_styles");
    return options.isSet("table_default_styles") ? defaultStyles : determineDefaultTableStyles(editor, defaultStyles);
  };
  const tableUseColumnGroup = option("table_use_colgroups");
  const fixedContainerSelector = option("fixed_toolbar_container");
  const fixedToolbarContainerTarget = option("fixed_toolbar_container_target");
  const fixedContainerTarget = editor => {
    var _a;
    if (!editor.inline) {
      return Optional.none();
    }
    const selector = (_a = fixedContainerSelector(editor)) !== null && _a !== void 0 ? _a : "";
    if (selector.length > 0) {
      return descendant(body$1(), selector);
    }
    const element = fixedToolbarContainerTarget(editor);
    if (isNonNullable(element)) {
      return Optional.some(SugarElement.fromDom(element));
    }
    return Optional.none();
  };
  const useFixedContainer = editor => editor.inline && fixedContainerTarget(editor).isSome();
  const getUiMode = option("ui_mode");
  const isSplitUiMode = editor => !useFixedContainer(editor) && getUiMode(editor) === "split";

  const closest = target => closest$1(target, "[contenteditable]");
  const isEditable$1 = (element, assumeEditable = false) => {
    if (inBody(element)) {
      return element.dom.isContentEditable;
    } else {
      return closest(element).fold(constant(assumeEditable), editable => getRaw(editable) === "true");
    }
  };
  const getRaw = element => element.dom.contentEditable;

  const getBody = editor => SugarElement.fromDom(editor.getBody());
  const getIsRoot = editor => element => eq$1(element, getBody(editor));
  const removeDataStyle = table => {
    remove$7(table, "data-mce-style");
    const removeStyleAttribute = element => remove$7(element, "data-mce-style");
    each$2(cells$1(table), removeStyleAttribute);
    each$2(columns$1(table), removeStyleAttribute);
    each$2(rows$1(table), removeStyleAttribute);
  };
  const getSelectionStart = editor => SugarElement.fromDom(editor.selection.getStart());
  const getPixelWidth = elm => elm.getBoundingClientRect().width;
  const getPixelHeight = elm => elm.getBoundingClientRect().height;
  const getRawValue = prop => (editor, elm) => {
    const raw = editor.dom.getStyle(elm, prop) || editor.dom.getAttrib(elm, prop);
    return Optional.from(raw).filter(isNotEmpty);
  };
  const getRawWidth = getRawValue("width");
  const getRawHeight = getRawValue("height");
  const isPercentage$1 = value => /^(\d+(\.\d+)?)%$/.test(value);
  const isPixel = value => /^(\d+(\.\d+)?)px$/.test(value);
  const isInEditableContext$1 = cell => closest$2(cell, isTag("table")).exists(isEditable$1);

  const inSelection = (bounds, detail) => {
    const leftEdge = detail.column;
    const rightEdge = detail.column + detail.colspan - 1;
    const topEdge = detail.row;
    const bottomEdge = detail.row + detail.rowspan - 1;
    return leftEdge <= bounds.finishCol && rightEdge >= bounds.startCol && (topEdge <= bounds.finishRow && bottomEdge >= bounds.startRow);
  };
  const isWithin = (bounds, detail) => {
    return detail.column >= bounds.startCol && detail.column + detail.colspan - 1 <= bounds.finishCol && detail.row >= bounds.startRow && detail.row + detail.rowspan - 1 <= bounds.finishRow;
  };
  const isRectangular = (warehouse, bounds) => {
    let isRect = true;
    const detailIsWithin = curry(isWithin, bounds);
    for (let i = bounds.startRow; i <= bounds.finishRow; i++) {
      for (let j = bounds.startCol; j <= bounds.finishCol; j++) {
        isRect = isRect && Warehouse.getAt(warehouse, i, j).exists(detailIsWithin);
      }
    }
    return isRect ? Optional.some(bounds) : Optional.none();
  };

  const getBounds = (detailA, detailB) => {
    return bounds(Math.min(detailA.row, detailB.row), Math.min(detailA.column, detailB.column), Math.max(detailA.row + detailA.rowspan - 1, detailB.row + detailB.rowspan - 1), Math.max(detailA.column + detailA.colspan - 1, detailB.column + detailB.colspan - 1));
  };
  const getAnyBox = (warehouse, startCell, finishCell) => {
    const startCoords = Warehouse.findItem(warehouse, startCell, eq$1);
    const finishCoords = Warehouse.findItem(warehouse, finishCell, eq$1);
    return startCoords.bind(sc => {
      return finishCoords.map(fc => {
        return getBounds(sc, fc);
      });
    });
  };
  const getBox$1 = (warehouse, startCell, finishCell) => {
    return getAnyBox(warehouse, startCell, finishCell).bind(bounds => {
      return isRectangular(warehouse, bounds);
    });
  };

  const moveBy$1 = (warehouse, cell, row, column) => {
    return Warehouse.findItem(warehouse, cell, eq$1).bind(detail => {
      const startRow = row > 0 ? detail.row + detail.rowspan - 1 : detail.row;
      const startCol = column > 0 ? detail.column + detail.colspan - 1 : detail.column;
      const dest = Warehouse.getAt(warehouse, startRow + row, startCol + column);
      return dest.map(d => {
        return d.element;
      });
    });
  };
  const intercepts$1 = (warehouse, start, finish) => {
    return getAnyBox(warehouse, start, finish).map(bounds => {
      const inside = Warehouse.filterItems(warehouse, curry(inSelection, bounds));
      return map$1(inside, detail => {
        return detail.element;
      });
    });
  };
  const parentCell = (warehouse, innerCell) => {
    const isContainedBy = (c1, c2) => {
      return contains$1(c2, c1);
    };
    return Warehouse.findItem(warehouse, innerCell, isContainedBy).map(detail => {
      return detail.element;
    });
  };

  const moveBy = (cell, deltaRow, deltaColumn) => {
    return table(cell).bind(table => {
      const warehouse = getWarehouse(table);
      return moveBy$1(warehouse, cell, deltaRow, deltaColumn);
    });
  };
  const intercepts = (table, first, last) => {
    const warehouse = getWarehouse(table);
    return intercepts$1(warehouse, first, last);
  };
  const nestedIntercepts = (table, first, firstTable, last, lastTable) => {
    const warehouse = getWarehouse(table);
    const optStartCell = eq$1(table, firstTable) ? Optional.some(first) : parentCell(warehouse, first);
    const optLastCell = eq$1(table, lastTable) ? Optional.some(last) : parentCell(warehouse, last);
    return optStartCell.bind(startCell => optLastCell.bind(lastCell => intercepts$1(warehouse, startCell, lastCell)));
  };
  const getBox = (table, first, last) => {
    const warehouse = getWarehouse(table);
    return getBox$1(warehouse, first, last);
  };
  const getWarehouse = Warehouse.fromTable;

  var TagBoundaries = [
    "body",
    "p",
    "div",
    "article",
    "aside",
    "figcaption",
    "figure",
    "footer",
    "header",
    "nav",
    "section",
    "ol",
    "ul",
    "li",
    "table",
    "thead",
    "tbody",
    "tfoot",
    "caption",
    "tr",
    "td",
    "th",
    "h1",
    "h2",
    "h3",
    "h4",
    "h5",
    "h6",
    "blockquote",
    "pre",
    "address"
  ];

  var DomUniverse = () => {
    const clone = element => {
      return SugarElement.fromDom(element.dom.cloneNode(false));
    };
    const document = element => documentOrOwner(element).dom;
    const isBoundary = element => {
      if (!isElement(element)) {
        return false;
      }
      if (name(element) === "body") {
        return true;
      }
      return contains$2(TagBoundaries, name(element));
    };
    const isEmptyTag = element => {
      if (!isElement(element)) {
        return false;
      }
      return contains$2([
        "br",
        "img",
        "hr",
        "input"
      ], name(element));
    };
    const isNonEditable = element => isElement(element) && get$b(element, "contenteditable") === "false";
    const comparePosition = (element, other) => {
      return element.dom.compareDocumentPosition(other.dom);
    };
    const copyAttributesTo = (source, destination) => {
      const as = clone$2(source);
      setAll$1(destination, as);
    };
    const isSpecial = element => {
      const tag = name(element);
      return contains$2([
        "script",
        "noscript",
        "iframe",
        "noframes",
        "noembed",
        "title",
        "style",
        "textarea",
        "xmp"
      ], tag);
    };
    const getLanguage = element => isElement(element) ? getOpt(element, "lang") : Optional.none();
    return {
      up: constant({
        selector: ancestor$1,
        closest: closest$1,
        predicate: ancestor$2,
        all: parents
      }),
      down: constant({
        selector: descendants,
        predicate: descendants$1
      }),
      styles: constant({
        get: get$a,
        getRaw: getRaw$2,
        set: set$1,
        remove: remove$5
      }),
      attrs: constant({
        get: get$b,
        set: set$2,
        remove: remove$7,
        copyTo: copyAttributesTo
      }),
      insert: constant({
        before: before$3,
        after: after$5,
        afterAll: after$4,
        append: append$1,
        appendAll: append,
        prepend: prepend,
        wrap: wrap
      }),
      remove: constant({
        unwrap: unwrap,
        remove: remove$6
      }),
      create: constant({
        nu: SugarElement.fromTag,
        clone,
        text: SugarElement.fromText
      }),
      query: constant({
        comparePosition,
        prevSibling: prevSibling,
        nextSibling: nextSibling
      }),
      property: constant({
        children: children$2,
        name: name,
        parent: parent,
        document,
        isText: isText,
        isComment: isComment,
        isElement: isElement,
        isSpecial,
        getLanguage,
        getText: get$6,
        setText: set,
        isBoundary,
        isEmptyTag,
        isNonEditable
      }),
      eq: eq$1,
      is: is$1
    };
  };

  const all = (universe, look, elements, f) => {
    const head = elements[0];
    const tail = elements.slice(1);
    return f(universe, look, head, tail);
  };
  const oneAll = (universe, look, elements) => {
    return elements.length > 0 ? all(universe, look, elements, unsafeOne) : Optional.none();
  };
  const unsafeOne = (universe, look, head, tail) => {
    const start = look(universe, head);
    return foldr(tail, (b, a) => {
      const current = look(universe, a);
      return commonElement(universe, b, current);
    }, start);
  };
  const commonElement = (universe, start, end) => {
    return start.bind(s => {
      return end.filter(curry(universe.eq, s));
    });
  };

  const eq = (universe, item) => {
    return curry(universe.eq, item);
  };
  const ancestors$2 = (universe, start, end, isRoot = never) => {
    const ps1 = [start].concat(universe.up().all(start));
    const ps2 = [end].concat(universe.up().all(end));
    const prune = path => {
      const index = findIndex(path, isRoot);
      return index.fold(() => {
        return path;
      }, ind => {
        return path.slice(0, ind + 1);
      });
    };
    const pruned1 = prune(ps1);
    const pruned2 = prune(ps2);
    const shared = find$1(pruned1, x => {
      return exists(pruned2, eq(universe, x));
    });
    return {
      firstpath: pruned1,
      secondpath: pruned2,
      shared
    };
  };

  const sharedOne$1 = oneAll;
  const ancestors$1 = ancestors$2;

  const universe$3 = DomUniverse();
  const sharedOne = (look, elements) => {
    return sharedOne$1(universe$3, (_universe, element) => {
      return look(element);
    }, elements);
  };
  const ancestors = (start, finish, isRoot) => {
    return ancestors$1(universe$3, start, finish, isRoot);
  };

  const lookupTable = container => {
    return ancestor$1(container, "table");
  };
  const identify = (start, finish, isRoot) => {
    const getIsRoot = rootTable => {
      return element => {
        return isRoot !== undefined && isRoot(element) || eq$1(element, rootTable);
      };
    };
    if (eq$1(start, finish)) {
      return Optional.some({
        boxes: Optional.some([start]),
        start,
        finish
      });
    } else {
      return lookupTable(start).bind(startTable => {
        return lookupTable(finish).bind(finishTable => {
          if (eq$1(startTable, finishTable)) {
            return Optional.some({
              boxes: intercepts(startTable, start, finish),
              start,
              finish
            });
          } else if (contains$1(startTable, finishTable)) {
            const ancestorCells = ancestors$3(finish, "td,th", getIsRoot(startTable));
            const finishCell = ancestorCells.length > 0 ? ancestorCells[ancestorCells.length - 1] : finish;
            return Optional.some({
              boxes: nestedIntercepts(startTable, start, startTable, finish, finishTable),
              start,
              finish: finishCell
            });
          } else if (contains$1(finishTable, startTable)) {
            const ancestorCells = ancestors$3(start, "td,th", getIsRoot(finishTable));
            const startCell = ancestorCells.length > 0 ? ancestorCells[ancestorCells.length - 1] : start;
            return Optional.some({
              boxes: nestedIntercepts(finishTable, start, startTable, finish, finishTable),
              start,
              finish: startCell
            });
          } else {
            return ancestors(start, finish).shared.bind(lca => {
              return closest$1(lca, "table", isRoot).bind(lcaTable => {
                const finishAncestorCells = ancestors$3(finish, "td,th", getIsRoot(lcaTable));
                const finishCell = finishAncestorCells.length > 0 ? finishAncestorCells[finishAncestorCells.length - 1] : finish;
                const startAncestorCells = ancestors$3(start, "td,th", getIsRoot(lcaTable));
                const startCell = startAncestorCells.length > 0 ? startAncestorCells[startAncestorCells.length - 1] : start;
                return Optional.some({
                  boxes: nestedIntercepts(lcaTable, start, startTable, finish, finishTable),
                  start: startCell,
                  finish: finishCell
                });
              });
            });
          }
        });
      });
    }
  };
  const retrieve$1 = (container, selector) => {
    const sels = descendants(container, selector);
    return sels.length > 0 ? Optional.some(sels) : Optional.none();
  };
  const getLast = (boxes, lastSelectedSelector) => {
    return find$1(boxes, box => {
      return is$2(box, lastSelectedSelector);
    });
  };
  const getEdges = (container, firstSelectedSelector, lastSelectedSelector) => {
    return descendant(container, firstSelectedSelector).bind(first => {
      return descendant(container, lastSelectedSelector).bind(last => {
        return sharedOne(lookupTable, [
          first,
          last
        ]).map(table => {
          return {
            first,
            last,
            table
          };
        });
      });
    });
  };
  const expandTo = (finish, firstSelectedSelector) => {
    return ancestor$1(finish, "table").bind(table => {
      return descendant(table, firstSelectedSelector).bind(start => {
        return identify(start, finish).bind(identified => {
          return identified.boxes.map(boxes => {
            return {
              boxes,
              start: identified.start,
              finish: identified.finish
            };
          });
        });
      });
    });
  };
  const shiftSelection = (boxes, deltaRow, deltaColumn, firstSelectedSelector, lastSelectedSelector) => {
    return getLast(boxes, lastSelectedSelector).bind(last => {
      return moveBy(last, deltaRow, deltaColumn).bind(finish => {
        return expandTo(finish, firstSelectedSelector);
      });
    });
  };

  const retrieve = (container, selector) => {
    return retrieve$1(container, selector);
  };
  const retrieveBox = (container, firstSelectedSelector, lastSelectedSelector) => {
    return getEdges(container, firstSelectedSelector, lastSelectedSelector).bind(edges => {
      const isRoot = ancestor => {
        return eq$1(container, ancestor);
      };
      const sectionSelector = "thead,tfoot,tbody,table";
      const firstAncestor = ancestor$1(edges.first, sectionSelector, isRoot);
      const lastAncestor = ancestor$1(edges.last, sectionSelector, isRoot);
      return firstAncestor.bind(fA => {
        return lastAncestor.bind(lA => {
          return eq$1(fA, lA) ? getBox(edges.table, edges.first, edges.last) : Optional.none();
        });
      });
    });
  };

  const selection = identity;
  const unmergable = selectedCells => {
    const hasSpan = (elem, type) => getOpt(elem, type).exists(span => parseInt(span, 10) > 1);
    const hasRowOrColSpan = elem => hasSpan(elem, "rowspan") || hasSpan(elem, "colspan");
    return selectedCells.length > 0 && forall(selectedCells, hasRowOrColSpan) ? Optional.some(selectedCells) : Optional.none();
  };
  const mergable = (table, selectedCells, ephemera) => {
    if (selectedCells.length <= 1) {
      return Optional.none();
    } else {
      return retrieveBox(table, ephemera.firstSelectedSelector, ephemera.lastSelectedSelector).map(bounds => ({
        bounds,
        cells: selectedCells
      }));
    }
  };

  const strSelected = "data-mce-selected";
  const strSelectedSelector = "td[" + strSelected + "],th[" + strSelected + "]";
  const strAttributeSelector = "[" + strSelected + "]";
  const strFirstSelected = "data-mce-first-selected";
  const strFirstSelectedSelector = "td[" + strFirstSelected + "],th[" + strFirstSelected + "]";
  const strLastSelected = "data-mce-last-selected";
  const strLastSelectedSelector = "td[" + strLastSelected + "],th[" + strLastSelected + "]";
  const attributeSelector = strAttributeSelector;
  const ephemera = {
    selected: strSelected,
    selectedSelector: strSelectedSelector,
    firstSelected: strFirstSelected,
    firstSelectedSelector: strFirstSelectedSelector,
    lastSelected: strLastSelected,
    lastSelectedSelector: strLastSelectedSelector
  };

  const forMenu = (selectedCells, table, cell) => ({
    element: cell,
    mergable: mergable(table, selectedCells, ephemera),
    unmergable: unmergable(selectedCells),
    selection: selection(selectedCells)
  });
  const paste = (element, clipboard, generators) => ({
    element,
    clipboard,
    generators
  });
  const pasteRows = (selectedCells, _cell, clipboard, generators) => ({
    selection: selection(selectedCells),
    clipboard,
    generators
  });

  const getSelectionCellFallback = element => table(element).bind(table => retrieve(table, ephemera.firstSelectedSelector)).fold(constant(element), cells => cells[0]);
  const getSelectionFromSelector = selector => (initCell, isRoot) => {
    const cellName = name(initCell);
    const cell = cellName === "col" || cellName === "colgroup" ? getSelectionCellFallback(initCell) : initCell;
    return closest$1(cell, selector, isRoot);
  };
  const getSelectionCellOrCaption = getSelectionFromSelector("th,td,caption");
  const getSelectionCell = getSelectionFromSelector("th,td");
  const getCellsFromSelection = editor => fromDom(editor.model.table.getSelectedCells());
  const getCellsFromFakeSelection = editor => filter$2(getCellsFromSelection(editor), cell => is$2(cell, ephemera.selectedSelector));

  const extractSelected = cells => {
    return table(cells[0]).map(table => {
      const replica = extract$1(table, attributeSelector);
      removeDataStyle(replica);
      return [replica];
    });
  };
  const serializeElements = (editor, elements) => map$1(elements, elm => editor.selection.serializer.serialize(elm.dom, {})).join("");
  const getTextContent = (editor, replicaElements) => {
    const doc = editor.getDoc();
    const dos = getRootNode(SugarElement.fromDom(editor.getBody()));
    const offscreenDiv = SugarElement.fromTag("div", doc);
    set$2(offscreenDiv, "data-mce-bogus", "all");
    setAll(offscreenDiv, {
      position: "fixed",
      left: "-9999999px",
      top: "0",
      overflow: "hidden",
      opacity: "0"
    });
    const root = getContentContainer(dos);
    append(offscreenDiv, replicaElements);
    append$1(root, offscreenDiv);
    const textContent = offscreenDiv.dom.innerText;
    remove$6(offscreenDiv);
    return textContent;
  };
  const registerEvents = (editor, actions) => {
    editor.on("BeforeGetContent", e => {
      const multiCellContext = cells => {
        e.preventDefault();
        extractSelected(cells).each(replicaElements => {
          const content = e.format === "text" ? getTextContent(editor, replicaElements) : serializeElements(editor, replicaElements);
          e.content = content;
        });
      };
      if (e.selection === true) {
        const cells = getCellsFromFakeSelection(editor);
        if (cells.length >= 1) {
          multiCellContext(cells);
        }
      }
    });
    editor.on("BeforeSetContent", e => {
      if (e.selection === true && e.paste === true) {
        const selectedCells = getCellsFromSelection(editor);
        head(selectedCells).each(cell => {
          table(cell).each(table => {
            const elements = filter$2(fromHtml(e.content), content => {
              return name(content) !== "meta";
            });
            const isTable = isTag("table");
            if (shouldMergeContentOnPaste(editor) && elements.length === 1 && isTable(elements[0])) {
              e.preventDefault();
              const doc = SugarElement.fromDom(editor.getDoc());
              const generators = paste$1(doc);
              const targets = paste(cell, elements[0], generators);
              actions.pasteCells(table, targets).each(() => {
                editor.focus();
              });
            }
          });
        });
      }
    });
  };

  const point = (element, offset) => ({
    element,
    offset
  });

  const scan$1 = (universe, element, direction) => {
    if (universe.property().isText(element) && universe.property().getText(element).trim().length === 0 || universe.property().isComment(element)) {
      return direction(element).bind(elem => {
        return scan$1(universe, elem, direction).orThunk(() => {
          return Optional.some(elem);
        });
      });
    } else {
      return Optional.none();
    }
  };
  const toEnd = (universe, element) => {
    if (universe.property().isText(element)) {
      return universe.property().getText(element).length;
    }
    const children = universe.property().children(element);
    return children.length;
  };
  const freefallRtl$2 = (universe, element) => {
    const candidate = scan$1(universe, element, universe.query().prevSibling).getOr(element);
    if (universe.property().isText(candidate)) {
      return point(candidate, toEnd(universe, candidate));
    }
    const children = universe.property().children(candidate);
    return children.length > 0 ? freefallRtl$2(universe, children[children.length - 1]) : point(candidate, toEnd(universe, candidate));
  };

  const freefallRtl$1 = freefallRtl$2;

  const universe$2 = DomUniverse();
  const freefallRtl = element => {
    return freefallRtl$1(universe$2, element);
  };

  const halve = (main, other) => {
    if (!hasColspan(main)) {
      const width = getGenericWidth(main);
      width.each(w => {
        const newWidth = w.value / 2;
        setGenericWidth(main, newWidth, w.unit);
        setGenericWidth(other, newWidth, w.unit);
      });
    }
  };

  const zero = array => map$1(array, constant(0));
  const surround = (sizes, startIndex, endIndex, results, f) => f(sizes.slice(0, startIndex)).concat(results).concat(f(sizes.slice(endIndex)));
  const clampDeltaHelper = predicate => (sizes, index, delta, minCellSize) => {
    if (!predicate(delta)) {
      return delta;
    } else {
      const newSize = Math.max(minCellSize, sizes[index] - Math.abs(delta));
      const diff = Math.abs(newSize - sizes[index]);
      return delta >= 0 ? diff : -diff;
    }
  };
  const clampNegativeDelta = clampDeltaHelper(delta => delta < 0);
  const clampDelta = clampDeltaHelper(always);
  const resizeTable = () => {
    const calcFixedDeltas = (sizes, index, next, delta, minCellSize) => {
      const clampedDelta = clampNegativeDelta(sizes, index, delta, minCellSize);
      return surround(sizes, index, next + 1, [
        clampedDelta,
        0
      ], zero);
    };
    const calcRelativeDeltas = (sizes, index, delta, minCellSize) => {
      const ratio = (100 + delta) / 100;
      const newThis = Math.max(minCellSize, (sizes[index] + delta) / ratio);
      return map$1(sizes, (size, idx) => {
        const newSize = idx === index ? newThis : size / ratio;
        return newSize - size;
      });
    };
    const calcLeftEdgeDeltas = (sizes, index, next, delta, minCellSize, isRelative) => {
      if (isRelative) {
        return calcRelativeDeltas(sizes, index, delta, minCellSize);
      } else {
        return calcFixedDeltas(sizes, index, next, delta, minCellSize);
      }
    };
    const calcMiddleDeltas = (sizes, _prev, index, next, delta, minCellSize, isRelative) => calcLeftEdgeDeltas(sizes, index, next, delta, minCellSize, isRelative);
    const resizeTable = (resizer, delta) => resizer(delta);
    const calcRightEdgeDeltas = (sizes, _prev, index, delta, minCellSize, isRelative) => {
      if (isRelative) {
        return calcRelativeDeltas(sizes, index, delta, minCellSize);
      } else {
        const clampedDelta = clampNegativeDelta(sizes, index, delta, minCellSize);
        return zero(sizes.slice(0, index)).concat([clampedDelta]);
      }
    };
    const calcRedestributedWidths = (sizes, totalWidth, pixelDelta, isRelative) => {
      if (isRelative) {
        const tableWidth = totalWidth + pixelDelta;
        const ratio = tableWidth / totalWidth;
        const newSizes = map$1(sizes, size => size / ratio);
        return {
          delta: ratio * 100 - 100,
          newSizes
        };
      } else {
        return {
          delta: pixelDelta,
          newSizes: sizes
        };
      }
    };
    return {
      resizeTable,
      clampTableDelta: clampNegativeDelta,
      calcLeftEdgeDeltas,
      calcMiddleDeltas,
      calcRightEdgeDeltas,
      calcRedestributedWidths
    };
  };
  const preserveTable = () => {
    const calcLeftEdgeDeltas = (sizes, index, next, delta, minCellSize) => {
      const idx = delta >= 0 ? next : index;
      const clampedDelta = clampDelta(sizes, idx, delta, minCellSize);
      return surround(sizes, index, next + 1, [
        clampedDelta,
        -clampedDelta
      ], zero);
    };
    const calcMiddleDeltas = (sizes, _prev, index, next, delta, minCellSize) => calcLeftEdgeDeltas(sizes, index, next, delta, minCellSize);
    const resizeTable = (resizer, delta, isLastColumn) => {
      if (isLastColumn) {
        resizer(delta);
      }
    };
    const calcRightEdgeDeltas = (sizes, _prev, _index, delta, _minCellSize, isRelative) => {
      if (isRelative) {
        return zero(sizes);
      } else {
        const diff = delta / sizes.length;
        return map$1(sizes, constant(diff));
      }
    };
    const clampTableDelta = (sizes, index, delta, minCellSize, isLastColumn) => {
      if (isLastColumn) {
        if (delta >= 0) {
          return delta;
        } else {
          const maxDelta = foldl(sizes, (a, b) => a + b - minCellSize, 0);
          return Math.max(-maxDelta, delta);
        }
      } else {
        return clampNegativeDelta(sizes, index, delta, minCellSize);
      }
    };
    const calcRedestributedWidths = (sizes, _totalWidth, _pixelDelta, _isRelative) => ({
      delta: 0,
      newSizes: sizes
    });
    return {
      resizeTable,
      clampTableDelta,
      calcLeftEdgeDeltas,
      calcMiddleDeltas,
      calcRightEdgeDeltas,
      calcRedestributedWidths
    };
  };

  const getGridSize = table => {
    const warehouse = Warehouse.fromTable(table);
    return warehouse.grid;
  };

  const isHeaderCell = isTag("th");
  const isHeaderCells = cells => forall(cells, cell => isHeaderCell(cell.element));
  const getRowHeaderType = (isHeaderRow, isHeaderCells) => {
    if (isHeaderRow && isHeaderCells) {
      return "sectionCells";
    } else if (isHeaderRow) {
      return "section";
    } else {
      return "cells";
    }
  };
  const getRowType = row => {
    const isHeaderRow = row.section === "thead";
    const isHeaderCells = is(findCommonCellType(row.cells), "th");
    if (row.section === "tfoot") {
      return { type: "footer" };
    } else if (isHeaderRow || isHeaderCells) {
      return {
        type: "header",
        subType: getRowHeaderType(isHeaderRow, isHeaderCells)
      };
    } else {
      return { type: "body" };
    }
  };
  const findCommonCellType = cells => {
    const headerCells = filter$2(cells, cell => isHeaderCell(cell.element));
    if (headerCells.length === 0) {
      return Optional.some("td");
    } else if (headerCells.length === cells.length) {
      return Optional.some("th");
    } else {
      return Optional.none();
    }
  };
  const findCommonRowType = rows => {
    const rowTypes = map$1(rows, row => getRowType(row).type);
    const hasHeader = contains$2(rowTypes, "header");
    const hasFooter = contains$2(rowTypes, "footer");
    if (!hasHeader && !hasFooter) {
      return Optional.some("body");
    } else {
      const hasBody = contains$2(rowTypes, "body");
      if (hasHeader && !hasBody && !hasFooter) {
        return Optional.some("header");
      } else if (!hasHeader && !hasBody && hasFooter) {
        return Optional.some("footer");
      } else {
        return Optional.none();
      }
    }
  };
  const findTableRowHeaderType = warehouse => findMap(warehouse.all, row => {
    const rowType = getRowType(row);
    return rowType.type === "header" ? Optional.from(rowType.subType) : Optional.none();
  });

  const transformCell = (cell, comparator, substitution) => elementnew(substitution(cell.element, comparator), true, cell.isLocked);
  const transformRow = (row, section) => row.section !== section ? rowcells(row.element, row.cells, section, row.isNew) : row;
  const section = () => ({
    transformRow,
    transformCell: (cell, comparator, substitution) => {
      const newCell = substitution(cell.element, comparator);
      const fixedCell = name(newCell) !== "td" ? mutate$1(newCell, "td") : newCell;
      return elementnew(fixedCell, cell.isNew, cell.isLocked);
    }
  });
  const sectionCells = () => ({
    transformRow,
    transformCell
  });
  const cells = () => ({
    transformRow: (row, section) => {
      const newSection = section === "thead" ? "tbody" : section;
      return transformRow(row, newSection);
    },
    transformCell
  });
  const fallback = () => ({
    transformRow: identity,
    transformCell
  });
  const getTableSectionType = (table, fallback) => {
    const warehouse = Warehouse.fromTable(table);
    const type = findTableRowHeaderType(warehouse).getOr(fallback);
    switch (type) {
      case "section":
        return section();
      case "sectionCells":
        return sectionCells();
      case "cells":
        return cells();
    }
  };
  const TableSection = {
    getTableSectionType,
    section,
    sectionCells,
    cells,
    fallback
  };

  const setIfNot = (element, property, value, ignore) => {
    if (value === ignore) {
      remove$7(element, property);
    } else {
      set$2(element, property, value);
    }
  };
  const insert$1 = (table, selector, element) => {
    last$2(children(table, selector)).fold(() => prepend(table, element), child => after$5(child, element));
  };
  const generateSection = (table, sectionName) => {
    const section = child(table, sectionName).getOrThunk(() => {
      const newSection = SugarElement.fromTag(sectionName, owner(table).dom);
      if (sectionName === "thead") {
        insert$1(table, "caption,colgroup", newSection);
      } else if (sectionName === "colgroup") {
        insert$1(table, "caption", newSection);
      } else {
        append$1(table, newSection);
      }
      return newSection;
    });
    empty(section);
    return section;
  };
  const render$1 = (table, grid) => {
    const newRows = [];
    const newCells = [];
    const syncRows = gridSection => map$1(gridSection, row => {
      if (row.isNew) {
        newRows.push(row.element);
      }
      const tr = row.element;
      empty(tr);
      each$2(row.cells, cell => {
        if (cell.isNew) {
          newCells.push(cell.element);
        }
        setIfNot(cell.element, "colspan", cell.colspan, 1);
        setIfNot(cell.element, "rowspan", cell.rowspan, 1);
        append$1(tr, cell.element);
      });
      return tr;
    });
    const syncColGroup = gridSection => bind$2(gridSection, colGroup => map$1(colGroup.cells, col => {
      setIfNot(col.element, "span", col.colspan, 1);
      return col.element;
    }));
    const renderSection = (gridSection, sectionName) => {
      const section = generateSection(table, sectionName);
      const sync = sectionName === "colgroup" ? syncColGroup : syncRows;
      const sectionElems = sync(gridSection);
      append(section, sectionElems);
    };
    const removeSection = sectionName => {
      child(table, sectionName).each(remove$6);
    };
    const renderOrRemoveSection = (gridSection, sectionName) => {
      if (gridSection.length > 0) {
        renderSection(gridSection, sectionName);
      } else {
        removeSection(sectionName);
      }
    };
    const headSection = [];
    const bodySection = [];
    const footSection = [];
    const columnGroupsSection = [];
    each$2(grid, row => {
      switch (row.section) {
        case "thead":
          headSection.push(row);
          break;
        case "tbody":
          bodySection.push(row);
          break;
        case "tfoot":
          footSection.push(row);
          break;
        case "colgroup":
          columnGroupsSection.push(row);
          break;
      }
    });
    renderOrRemoveSection(columnGroupsSection, "colgroup");
    renderOrRemoveSection(headSection, "thead");
    renderOrRemoveSection(bodySection, "tbody");
    renderOrRemoveSection(footSection, "tfoot");
    return {
      newRows,
      newCells
    };
  };
  const copy = grid => map$1(grid, row => {
    const tr = shallow(row.element);
    each$2(row.cells, cell => {
      const clonedCell = deep(cell.element);
      setIfNot(clonedCell, "colspan", cell.colspan, 1);
      setIfNot(clonedCell, "rowspan", cell.rowspan, 1);
      append$1(tr, clonedCell);
    });
    return tr;
  });

  const getColumn = (grid, index) => {
    return map$1(grid, row => {
      return getCell(row, index);
    });
  };
  const getRow = (grid, index) => {
    return grid[index];
  };
  const findDiff = (xs, comp) => {
    if (xs.length === 0) {
      return 0;
    }
    const first = xs[0];
    const index = findIndex(xs, x => {
      return !comp(first.element, x.element);
    });
    return index.getOr(xs.length);
  };
  const subgrid = (grid, row, column, comparator) => {
    const gridRow = getRow(grid, row);
    const isColRow = gridRow.section === "colgroup";
    const colspan = findDiff(gridRow.cells.slice(column), comparator);
    const rowspan = isColRow ? 1 : findDiff(getColumn(grid.slice(row), column), comparator);
    return {
      colspan,
      rowspan
    };
  };

  const toDetails = (grid, comparator) => {
    const seen = map$1(grid, row => map$1(row.cells, never));
    const updateSeen = (rowIndex, columnIndex, rowspan, colspan) => {
      for (let row = rowIndex; row < rowIndex + rowspan; row++) {
        for (let column = columnIndex; column < columnIndex + colspan; column++) {
          seen[row][column] = true;
        }
      }
    };
    return map$1(grid, (row, rowIndex) => {
      const details = bind$2(row.cells, (cell, columnIndex) => {
        if (seen[rowIndex][columnIndex] === false) {
          const result = subgrid(grid, rowIndex, columnIndex, comparator);
          updateSeen(rowIndex, columnIndex, result.rowspan, result.colspan);
          return [detailnew(cell.element, result.rowspan, result.colspan, cell.isNew)];
        } else {
          return [];
        }
      });
      return rowdetailnew(row.element, details, row.section, row.isNew);
    });
  };
  const toGrid = (warehouse, generators, isNew) => {
    const grid = [];
    each$2(warehouse.colgroups, colgroup => {
      const colgroupCols = [];
      for (let columnIndex = 0; columnIndex < warehouse.grid.columns; columnIndex++) {
        const element = Warehouse.getColumnAt(warehouse, columnIndex).map(column => elementnew(column.element, isNew, false)).getOrThunk(() => elementnew(generators.colGap(), true, false));
        colgroupCols.push(element);
      }
      grid.push(rowcells(colgroup.element, colgroupCols, "colgroup", isNew));
    });
    for (let rowIndex = 0; rowIndex < warehouse.grid.rows; rowIndex++) {
      const rowCells = [];
      for (let columnIndex = 0; columnIndex < warehouse.grid.columns; columnIndex++) {
        const element = Warehouse.getAt(warehouse, rowIndex, columnIndex).map(item => elementnew(item.element, isNew, item.isLocked)).getOrThunk(() => elementnew(generators.gap(), true, false));
        rowCells.push(element);
      }
      const rowDetail = warehouse.all[rowIndex];
      const row = rowcells(rowDetail.element, rowCells, rowDetail.section, isNew);
      grid.push(row);
    }
    return grid;
  };

  const fromWarehouse = (warehouse, generators) => toGrid(warehouse, generators, false);
  const toDetailList = grid => toDetails(grid, eq$1);
  const findInWarehouse = (warehouse, element) => findMap(warehouse.all, r => find$1(r.cells, e => eq$1(element, e.element)));
  const extractCells = (warehouse, target, predicate) => {
    const details = map$1(target.selection, cell$1 => {
      return cell(cell$1).bind(lc => findInWarehouse(warehouse, lc)).filter(predicate);
    });
    const cells = cat(details);
    return someIf(cells.length > 0, cells);
  };
  const run = (operation, extract, adjustment, postAction, genWrappers) => (table, target, generators, behaviours) => {
    const warehouse = Warehouse.fromTable(table);
    const tableSection = Optional.from(behaviours === null || behaviours === void 0 ? void 0 : behaviours.section).getOrThunk(TableSection.fallback);
    const output = extract(warehouse, target).map(info => {
      const model = fromWarehouse(warehouse, generators);
      const result = operation(model, info, eq$1, genWrappers(generators), tableSection);
      const lockedColumns = getLockedColumnsFromGrid(result.grid);
      const grid = toDetailList(result.grid);
      return {
        info,
        grid,
        cursor: result.cursor,
        lockedColumns
      };
    });
    return output.bind(out => {
      const newElements = render$1(table, out.grid);
      const tableSizing = Optional.from(behaviours === null || behaviours === void 0 ? void 0 : behaviours.sizing).getOrThunk(() => TableSize.getTableSize(table));
      const resizing = Optional.from(behaviours === null || behaviours === void 0 ? void 0 : behaviours.resize).getOrThunk(preserveTable);
      adjustment(table, out.grid, out.info, {
        sizing: tableSizing,
        resize: resizing,
        section: tableSection
      });
      postAction(table);
      remove$7(table, LOCKED_COL_ATTR);
      if (out.lockedColumns.length > 0) {
        set$2(table, LOCKED_COL_ATTR, out.lockedColumns.join(","));
      }
      return Optional.some({
        cursor: out.cursor,
        newRows: newElements.newRows,
        newCells: newElements.newCells
      });
    });
  };
  const onPaste = (warehouse, target) => cell(target.element).bind(cell => findInWarehouse(warehouse, cell).map(details => {
    const value = {
      ...details,
      generators: target.generators,
      clipboard: target.clipboard
    };
    return value;
  }));
  const onPasteByEditor = (warehouse, target) => extractCells(warehouse, target, always).map(cells => ({
    cells,
    generators: target.generators,
    clipboard: target.clipboard
  }));
  const onMergable = (_warehouse, target) => target.mergable;
  const onUnmergable = (_warehouse, target) => target.unmergable;
  const onCells = (warehouse, target) => extractCells(warehouse, target, always);
  const onUnlockedCells = (warehouse, target) => extractCells(warehouse, target, detail => !detail.isLocked);
  const isUnlockedTableCell = (warehouse, cell) => findInWarehouse(warehouse, cell).exists(detail => !detail.isLocked);
  const allUnlocked = (warehouse, cells) => forall(cells, cell => isUnlockedTableCell(warehouse, cell));
  const onUnlockedMergable = (warehouse, target) => onMergable(warehouse, target).filter(mergeable => allUnlocked(warehouse, mergeable.cells));
  const onUnlockedUnmergable = (warehouse, target) => onUnmergable(warehouse, target).filter(cells => allUnlocked(warehouse, cells));

  const merge$2 = (grid, bounds, comparator, substitution) => {
    const rows = extractGridDetails(grid).rows;
    if (rows.length === 0) {
      return grid;
    }
    for (let i = bounds.startRow; i <= bounds.finishRow; i++) {
      for (let j = bounds.startCol; j <= bounds.finishCol; j++) {
        const row = rows[i];
        const isLocked = getCell(row, j).isLocked;
        mutateCell(row, j, elementnew(substitution(), false, isLocked));
      }
    }
    return grid;
  };
  const unmerge = (grid, target, comparator, substitution) => {
    const rows = extractGridDetails(grid).rows;
    let first = true;
    for (let i = 0; i < rows.length; i++) {
      for (let j = 0; j < cellLength(rows[0]); j++) {
        const row = rows[i];
        const currentCell = getCell(row, j);
        const currentCellElm = currentCell.element;
        const isToReplace = comparator(currentCellElm, target);
        if (isToReplace && !first) {
          mutateCell(row, j, elementnew(substitution(), true, currentCell.isLocked));
        } else if (isToReplace) {
          first = false;
        }
      }
    }
    return grid;
  };
  const uniqueCells = (row, comparator) => {
    return foldl(row, (rest, cell) => {
      return exists(rest, currentCell => {
        return comparator(currentCell.element, cell.element);
      }) ? rest : rest.concat([cell]);
    }, []);
  };
  const splitCols = (grid, index, comparator, substitution) => {
    if (index > 0 && index < grid[0].cells.length) {
      each$2(grid, row => {
        const prevCell = row.cells[index - 1];
        let offset = 0;
        const substitute = substitution();
        while (row.cells.length > index + offset && comparator(prevCell.element, row.cells[index + offset].element)) {
          mutateCell(row, index + offset, elementnew(substitute, true, row.cells[index + offset].isLocked));
          offset++;
        }
      });
    }
    return grid;
  };
  const splitRows = (grid, index, comparator, substitution) => {
    const rows = extractGridDetails(grid).rows;
    if (index > 0 && index < rows.length) {
      const rowPrevCells = rows[index - 1].cells;
      const cells = uniqueCells(rowPrevCells, comparator);
      each$2(cells, cell => {
        let replacement = Optional.none();
        for (let i = index; i < rows.length; i++) {
          for (let j = 0; j < cellLength(rows[0]); j++) {
            const row = rows[i];
            const current = getCell(row, j);
            const isToReplace = comparator(current.element, cell.element);
            if (isToReplace) {
              if (replacement.isNone()) {
                replacement = Optional.some(substitution());
              }
              replacement.each(sub => {
                mutateCell(row, j, elementnew(sub, true, current.isLocked));
              });
            }
          }
        }
      });
    }
    return grid;
  };

  const value$1 = value => {
    const applyHelper = fn => fn(value);
    const constHelper = constant(value);
    const outputHelper = () => output;
    const output = {
      tag: true,
      inner: value,
      fold: (_onError, onValue) => onValue(value),
      isValue: always,
      isError: never,
      map: mapper => Result.value(mapper(value)),
      mapError: outputHelper,
      bind: applyHelper,
      exists: applyHelper,
      forall: applyHelper,
      getOr: constHelper,
      or: outputHelper,
      getOrThunk: constHelper,
      orThunk: outputHelper,
      getOrDie: constHelper,
      each: fn => {
        fn(value);
      },
      toOptional: () => Optional.some(value)
    };
    return output;
  };
  const error = error => {
    const outputHelper = () => output;
    const output = {
      tag: false,
      inner: error,
      fold: (onError, _onValue) => onError(error),
      isValue: never,
      isError: always,
      map: outputHelper,
      mapError: mapper => Result.error(mapper(error)),
      bind: outputHelper,
      exists: never,
      forall: always,
      getOr: identity,
      or: identity,
      getOrThunk: apply,
      orThunk: apply,
      getOrDie: die(String(error)),
      each: noop,
      toOptional: Optional.none
    };
    return output;
  };
  const fromOption = (optional, err) => optional.fold(() => error(err), value$1);
  const Result = {
    value: value$1,
    error,
    fromOption
  };

  const measure = (startAddress, gridA, gridB) => {
    if (startAddress.row >= gridA.length || startAddress.column > cellLength(gridA[0])) {
      return Result.error("invalid start address out of table bounds, row: " + startAddress.row + ", column: " + startAddress.column);
    }
    const rowRemainder = gridA.slice(startAddress.row);
    const colRemainder = rowRemainder[0].cells.slice(startAddress.column);
    const colRequired = cellLength(gridB[0]);
    const rowRequired = gridB.length;
    return Result.value({
      rowDelta: rowRemainder.length - rowRequired,
      colDelta: colRemainder.length - colRequired
    });
  };
  const measureWidth = (gridA, gridB) => {
    const colLengthA = cellLength(gridA[0]);
    const colLengthB = cellLength(gridB[0]);
    return {
      rowDelta: 0,
      colDelta: colLengthA - colLengthB
    };
  };
  const measureHeight = (gridA, gridB) => {
    const rowLengthA = gridA.length;
    const rowLengthB = gridB.length;
    return {
      rowDelta: rowLengthA - rowLengthB,
      colDelta: 0
    };
  };
  const generateElements = (amount, row, generators, isLocked) => {
    const generator = row.section === "colgroup" ? generators.col : generators.cell;
    return range$1(amount, idx => elementnew(generator(), true, isLocked(idx)));
  };
  const rowFill = (grid, amount, generators, lockedColumns) => {
    const exampleRow = grid[grid.length - 1];
    return grid.concat(range$1(amount, () => {
      const generator = exampleRow.section === "colgroup" ? generators.colgroup : generators.row;
      const row = clone(exampleRow, generator, identity);
      const elements = generateElements(row.cells.length, row, generators, idx => has$1(lockedColumns, idx.toString()));
      return setCells(row, elements);
    }));
  };
  const colFill = (grid, amount, generators, startIndex) => map$1(grid, row => {
    const newChildren = generateElements(amount, row, generators, never);
    return addCells(row, startIndex, newChildren);
  });
  const lockedColFill = (grid, generators, lockedColumns) => map$1(grid, row => {
    return foldl(lockedColumns, (acc, colNum) => {
      const newChild = generateElements(1, row, generators, always)[0];
      return addCell(acc, colNum, newChild);
    }, row);
  });
  const tailor = (gridA, delta, generators) => {
    const fillCols = delta.colDelta < 0 ? colFill : identity;
    const fillRows = delta.rowDelta < 0 ? rowFill : identity;
    const lockedColumns = getLockedColumnsFromGrid(gridA);
    const gridWidth = cellLength(gridA[0]);
    const isLastColLocked = exists(lockedColumns, locked => locked === gridWidth - 1);
    const modifiedCols = fillCols(gridA, Math.abs(delta.colDelta), generators, isLastColLocked ? gridWidth - 1 : gridWidth);
    const newLockedColumns = getLockedColumnsFromGrid(modifiedCols);
    return fillRows(modifiedCols, Math.abs(delta.rowDelta), generators, mapToObject(newLockedColumns, always));
  };

  const isSpanning = (grid, row, col, comparator) => {
    const candidate = getCell(grid[row], col);
    const matching = curry(comparator, candidate.element);
    const currentRow = grid[row];
    return grid.length > 1 && cellLength(currentRow) > 1 && (col > 0 && matching(getCellElement(currentRow, col - 1)) || col < currentRow.cells.length - 1 && matching(getCellElement(currentRow, col + 1)) || row > 0 && matching(getCellElement(grid[row - 1], col)) || row < grid.length - 1 && matching(getCellElement(grid[row + 1], col)));
  };
  const mergeTables = (startAddress, gridA, gridBRows, generator, comparator, lockedColumns) => {
    const startRow = startAddress.row;
    const startCol = startAddress.column;
    const mergeHeight = gridBRows.length;
    const mergeWidth = cellLength(gridBRows[0]);
    const endRow = startRow + mergeHeight;
    const endCol = startCol + mergeWidth + lockedColumns.length;
    const lockedColumnObj = mapToObject(lockedColumns, always);
    for (let r = startRow; r < endRow; r++) {
      let skippedCol = 0;
      for (let c = startCol; c < endCol; c++) {
        if (lockedColumnObj[c]) {
          skippedCol++;
          continue;
        }
        if (isSpanning(gridA, r, c, comparator)) {
          unmerge(gridA, getCellElement(gridA[r], c), comparator, generator.cell);
        }
        const gridBColIndex = c - startCol - skippedCol;
        const newCell = getCell(gridBRows[r - startRow], gridBColIndex);
        const newCellElm = newCell.element;
        const replacement = generator.replace(newCellElm);
        mutateCell(gridA[r], c, elementnew(replacement, true, newCell.isLocked));
      }
    }
    return gridA;
  };
  const getValidStartAddress = (currentStartAddress, grid, lockedColumns) => {
    const gridColLength = cellLength(grid[0]);
    const adjustedRowAddress = extractGridDetails(grid).cols.length + currentStartAddress.row;
    const possibleColAddresses = range$1(gridColLength - currentStartAddress.column, num => num + currentStartAddress.column);
    const validColAddress = find$1(possibleColAddresses, num => forall(lockedColumns, col => col !== num)).getOr(gridColLength - 1);
    return {
      row: adjustedRowAddress,
      column: validColAddress
    };
  };
  const getLockedColumnsWithinBounds = (startAddress, rows, lockedColumns) => filter$2(lockedColumns, colNum => colNum >= startAddress.column && colNum <= cellLength(rows[0]) + startAddress.column);
  const merge$1 = (startAddress, gridA, gridB, generator, comparator) => {
    const lockedColumns = getLockedColumnsFromGrid(gridA);
    const validStartAddress = getValidStartAddress(startAddress, gridA, lockedColumns);
    const gridBRows = extractGridDetails(gridB).rows;
    const lockedColumnsWithinBounds = getLockedColumnsWithinBounds(validStartAddress, gridBRows, lockedColumns);
    const result = measure(validStartAddress, gridA, gridBRows);
    return result.map(diff => {
      const delta = {
        ...diff,
        colDelta: diff.colDelta - lockedColumnsWithinBounds.length
      };
      const fittedGrid = tailor(gridA, delta, generator);
      const newLockedColumns = getLockedColumnsFromGrid(fittedGrid);
      const newLockedColumnsWithinBounds = getLockedColumnsWithinBounds(validStartAddress, gridBRows, newLockedColumns);
      return mergeTables(validStartAddress, fittedGrid, gridBRows, generator, comparator, newLockedColumnsWithinBounds);
    });
  };
  const insertCols = (index, gridA, gridB, generator, comparator) => {
    splitCols(gridA, index, comparator, generator.cell);
    const delta = measureHeight(gridB, gridA);
    const fittedNewGrid = tailor(gridB, delta, generator);
    const secondDelta = measureHeight(gridA, fittedNewGrid);
    const fittedOldGrid = tailor(gridA, secondDelta, generator);
    return map$1(fittedOldGrid, (gridRow, i) => {
      return addCells(gridRow, index, fittedNewGrid[i].cells);
    });
  };
  const insertRows = (index, gridA, gridB, generator, comparator) => {
    splitRows(gridA, index, comparator, generator.cell);
    const locked = getLockedColumnsFromGrid(gridA);
    const diff = measureWidth(gridA, gridB);
    const delta = {
      ...diff,
      colDelta: diff.colDelta - locked.length
    };
    const fittedOldGrid = tailor(gridA, delta, generator);
    const {
      cols: oldCols,
      rows: oldRows
    } = extractGridDetails(fittedOldGrid);
    const newLocked = getLockedColumnsFromGrid(fittedOldGrid);
    const secondDiff = measureWidth(gridB, gridA);
    const secondDelta = {
      ...secondDiff,
      colDelta: secondDiff.colDelta + newLocked.length
    };
    const fittedGridB = lockedColFill(gridB, generator, newLocked);
    const fittedNewGrid = tailor(fittedGridB, secondDelta, generator);
    return [
      ...oldCols,
      ...oldRows.slice(0, index),
      ...fittedNewGrid,
      ...oldRows.slice(index, oldRows.length)
    ];
  };

  const cloneRow = (row, cloneCell, comparator, substitution) => clone(row, elem => substitution(elem, comparator), cloneCell);
  const insertRowAt = (grid, index, example, comparator, substitution) => {
    const { rows, cols } = extractGridDetails(grid);
    const before = rows.slice(0, index);
    const after = rows.slice(index);
    const newRow = cloneRow(rows[example], (ex, c) => {
      const withinSpan = index > 0 && index < rows.length && comparator(getCellElement(rows[index - 1], c), getCellElement(rows[index], c));
      const ret = withinSpan ? getCell(rows[index], c) : elementnew(substitution(ex.element, comparator), true, ex.isLocked);
      return ret;
    }, comparator, substitution);
    return [
      ...cols,
      ...before,
      newRow,
      ...after
    ];
  };
  const getElementFor = (row, column, section, withinSpan, example, comparator, substitution) => {
    if (section === "colgroup" || !withinSpan) {
      const cell = getCell(row, example);
      return elementnew(substitution(cell.element, comparator), true, false);
    } else {
      return getCell(row, column);
    }
  };
  const insertColumnAt = (grid, index, example, comparator, substitution) => map$1(grid, row => {
    const withinSpan = index > 0 && index < cellLength(row) && comparator(getCellElement(row, index - 1), getCellElement(row, index));
    const sub = getElementFor(row, index, row.section, withinSpan, example, comparator, substitution);
    return addCell(row, index, sub);
  });
  const deleteColumnsAt = (grid, columns) => bind$2(grid, row => {
    const existingCells = row.cells;
    const cells = foldr(columns, (acc, column) => column >= 0 && column < acc.length ? acc.slice(0, column).concat(acc.slice(column + 1)) : acc, existingCells);
    return cells.length > 0 ? [rowcells(row.element, cells, row.section, row.isNew)] : [];
  });
  const deleteRowsAt = (grid, start, finish) => {
    const { rows, cols } = extractGridDetails(grid);
    return [
      ...cols,
      ...rows.slice(0, start),
      ...rows.slice(finish + 1)
    ];
  };

  const notInStartRow = (grid, rowIndex, colIndex, comparator) => getCellElement(grid[rowIndex], colIndex) !== undefined && (rowIndex > 0 && comparator(getCellElement(grid[rowIndex - 1], colIndex), getCellElement(grid[rowIndex], colIndex)));
  const notInStartColumn = (row, index, comparator) => index > 0 && comparator(getCellElement(row, index - 1), getCellElement(row, index));
  const isDuplicatedCell = (grid, rowIndex, colIndex, comparator) => notInStartRow(grid, rowIndex, colIndex, comparator) || notInStartColumn(grid[rowIndex], colIndex, comparator);
  const rowReplacerPredicate = (targetRow, columnHeaders) => {
    const entireTableIsHeader = forall(columnHeaders, identity) && isHeaderCells(targetRow.cells);
    return entireTableIsHeader ? always : (cell, _rowIndex, colIndex) => {
      const type = name(cell.element);
      return !(type === "th" && columnHeaders[colIndex]);
    };
  };
  const columnReplacePredicate = (targetColumn, rowHeaders) => {
    const entireTableIsHeader = forall(rowHeaders, identity) && isHeaderCells(targetColumn);
    return entireTableIsHeader ? always : (cell, rowIndex, _colIndex) => {
      const type = name(cell.element);
      return !(type === "th" && rowHeaders[rowIndex]);
    };
  };
  const determineScope = (applyScope, cell, newScope, isInHeader) => {
    const hasSpan = scope => scope === "row" ? hasRowspan(cell) : hasColspan(cell);
    const getScope = scope => hasSpan(scope) ? `${scope}group` : scope;
    if (applyScope) {
      return isHeaderCell(cell) ? getScope(newScope) : null;
    } else if (isInHeader && isHeaderCell(cell)) {
      const oppositeScope = newScope === "row" ? "col" : "row";
      return getScope(oppositeScope);
    } else {
      return null;
    }
  };
  const rowScopeGenerator = (applyScope, columnHeaders) => (cell, rowIndex, columnIndex) => Optional.some(determineScope(applyScope, cell.element, "col", columnHeaders[columnIndex]));
  const columnScopeGenerator = (applyScope, rowHeaders) => (cell, rowIndex) => Optional.some(determineScope(applyScope, cell.element, "row", rowHeaders[rowIndex]));
  const replace = (cell, comparator, substitute) => elementnew(substitute(cell.element, comparator), true, cell.isLocked);
  const replaceIn = (grid, targets, comparator, substitute, replacer, genScope, shouldReplace) => {
    const isTarget = cell => {
      return exists(targets, target => {
        return comparator(cell.element, target.element);
      });
    };
    return map$1(grid, (row, rowIndex) => {
      return mapCells(row, (cell, colIndex) => {
        if (isTarget(cell)) {
          const newCell = shouldReplace(cell, rowIndex, colIndex) ? replacer(cell, comparator, substitute) : cell;
          genScope(newCell, rowIndex, colIndex).each(scope => {
            setOptions(newCell.element, { scope: Optional.from(scope) });
          });
          return newCell;
        } else {
          return cell;
        }
      });
    });
  };
  const getColumnCells = (rows, columnIndex, comparator) => bind$2(rows, (row, i) => {
    return isDuplicatedCell(rows, i, columnIndex, comparator) ? [] : [getCell(row, columnIndex)];
  });
  const getRowCells = (rows, rowIndex, comparator) => {
    const targetRow = rows[rowIndex];
    return bind$2(targetRow.cells, (item, i) => {
      return isDuplicatedCell(rows, rowIndex, i, comparator) ? [] : [item];
    });
  };
  const replaceColumns = (grid, indexes, applyScope, comparator, substitution) => {
    const rows = extractGridDetails(grid).rows;
    const targets = bind$2(indexes, index => getColumnCells(rows, index, comparator));
    const rowHeaders = map$1(rows, row => isHeaderCells(row.cells));
    const shouldReplaceCell = columnReplacePredicate(targets, rowHeaders);
    const scopeGenerator = columnScopeGenerator(applyScope, rowHeaders);
    return replaceIn(grid, targets, comparator, substitution, replace, scopeGenerator, shouldReplaceCell);
  };
  const replaceRows = (grid, indexes, section, applyScope, comparator, substitution, tableSection) => {
    const { cols, rows } = extractGridDetails(grid);
    const targetRow = rows[indexes[0]];
    const targets = bind$2(indexes, index => getRowCells(rows, index, comparator));
    const columnHeaders = map$1(targetRow.cells, (_cell, index) => isHeaderCells(getColumnCells(rows, index, comparator)));
    const newRows = [...rows];
    each$2(indexes, index => {
      newRows[index] = tableSection.transformRow(rows[index], section);
    });
    const newGrid = [
      ...cols,
      ...newRows
    ];
    const shouldReplaceCell = rowReplacerPredicate(targetRow, columnHeaders);
    const scopeGenerator = rowScopeGenerator(applyScope, columnHeaders);
    return replaceIn(newGrid, targets, comparator, substitution, tableSection.transformCell, scopeGenerator, shouldReplaceCell);
  };
  const replaceCells = (grid, details, comparator, substitution) => {
    const rows = extractGridDetails(grid).rows;
    const targetCells = map$1(details, detail => getCell(rows[detail.row], detail.column));
    return replaceIn(grid, targetCells, comparator, substitution, replace, Optional.none, always);
  };

  const generate$1 = cases => {
    if (!isArray(cases)) {
      throw new Error("cases must be an array");
    }
    if (cases.length === 0) {
      throw new Error("there must be at least one case");
    }
    const constructors = [];
    const adt = {};
    each$2(cases, (acase, count) => {
      const keys$1 = keys(acase);
      if (keys$1.length !== 1) {
        throw new Error("one and only one name per case");
      }
      const key = keys$1[0];
      const value = acase[key];
      if (adt[key] !== undefined) {
        throw new Error("duplicate key detected:" + key);
      } else if (key === "cata") {
        throw new Error("cannot have a case named cata (sorry)");
      } else if (!isArray(value)) {
        throw new Error("case arguments must be an array");
      }
      constructors.push(key);
      adt[key] = (...args) => {
        const argLength = args.length;
        if (argLength !== value.length) {
          throw new Error("Wrong number of arguments to case " + key + ". Expected " + value.length + " (" + value + "), got " + argLength);
        }
        const match = branches => {
          const branchKeys = keys(branches);
          if (constructors.length !== branchKeys.length) {
            throw new Error("Wrong number of arguments to match. Expected: " + constructors.join(",") + "\nActual: " + branchKeys.join(","));
          }
          const allReqd = forall(constructors, reqKey => {
            return contains$2(branchKeys, reqKey);
          });
          if (!allReqd) {
            throw new Error("Not all branches were specified when using match. Specified: " + branchKeys.join(", ") + "\nRequired: " + constructors.join(", "));
          }
          return branches[key].apply(null, args);
        };
        return {
          fold: (...foldArgs) => {
            if (foldArgs.length !== cases.length) {
              throw new Error("Wrong number of arguments to fold. Expected " + cases.length + ", got " + foldArgs.length);
            }
            const target = foldArgs[count];
            return target.apply(null, args);
          },
          match,
          log: label => {
            console.log(label, {
              constructors,
              constructor: key,
              params: args
            });
          }
        };
      };
    });
    return adt;
  };
  const Adt = { generate: generate$1 };

  const adt$6 = Adt.generate([
    { none: [] },
    { only: ["index"] },
    {
      left: [
        "index",
        "next"
      ]
    },
    {
      middle: [
        "prev",
        "index",
        "next"
      ]
    },
    {
      right: [
        "prev",
        "index"
      ]
    }
  ]);
  const ColumnContext = { ...adt$6 };

  const neighbours = (input, index) => {
    if (input.length === 0) {
      return ColumnContext.none();
    }
    if (input.length === 1) {
      return ColumnContext.only(0);
    }
    if (index === 0) {
      return ColumnContext.left(0, 1);
    }
    if (index === input.length - 1) {
      return ColumnContext.right(index - 1, index);
    }
    if (index > 0 && index < input.length - 1) {
      return ColumnContext.middle(index - 1, index, index + 1);
    }
    return ColumnContext.none();
  };
  const determine = (input, column, step, tableSize, resize) => {
    const result = input.slice(0);
    const context = neighbours(input, column);
    const onNone = constant(map$1(result, constant(0)));
    const onOnly = index => tableSize.singleColumnWidth(result[index], step);
    const onLeft = (index, next) => resize.calcLeftEdgeDeltas(result, index, next, step, tableSize.minCellWidth(), tableSize.isRelative);
    const onMiddle = (prev, index, next) => resize.calcMiddleDeltas(result, prev, index, next, step, tableSize.minCellWidth(), tableSize.isRelative);
    const onRight = (prev, index) => resize.calcRightEdgeDeltas(result, prev, index, step, tableSize.minCellWidth(), tableSize.isRelative);
    return context.fold(onNone, onOnly, onLeft, onMiddle, onRight);
  };

  const total = (start, end, measures) => {
    let r = 0;
    for (let i = start; i < end; i++) {
      r += measures[i] !== undefined ? measures[i] : 0;
    }
    return r;
  };
  const recalculateWidthForCells = (warehouse, widths) => {
    const all = Warehouse.justCells(warehouse);
    return map$1(all, cell => {
      const width = total(cell.column, cell.column + cell.colspan, widths);
      return {
        element: cell.element,
        width,
        colspan: cell.colspan
      };
    });
  };
  const recalculateWidthForColumns = (warehouse, widths) => {
    const groups = Warehouse.justColumns(warehouse);
    return map$1(groups, (column, index) => ({
      element: column.element,
      width: widths[index],
      colspan: column.colspan
    }));
  };
  const matchRowHeight = (warehouse, heights) => {
    return map$1(warehouse.all, (row, i) => {
      return {
        element: row.element,
        height: heights[i]
      };
    });
  };

  const sumUp = newSize => foldr(newSize, (b, a) => b + a, 0);
  const recalculate = (warehouse, widths) => {
    if (Warehouse.hasColumns(warehouse)) {
      return recalculateWidthForColumns(warehouse, widths);
    } else {
      return recalculateWidthForCells(warehouse, widths);
    }
  };
  const recalculateAndApply = (warehouse, widths, tableSize) => {
    const newSizes = recalculate(warehouse, widths);
    each$2(newSizes, cell => {
      tableSize.setElementWidth(cell.element, cell.width);
    });
  };
  const adjustWidth = (table, delta, index, resizing, tableSize) => {
    const warehouse = Warehouse.fromTable(table);
    const step = tableSize.getCellDelta(delta);
    const widths = tableSize.getWidths(warehouse, tableSize);
    const isLastColumn = index === warehouse.grid.columns - 1;
    const clampedStep = resizing.clampTableDelta(widths, index, step, tableSize.minCellWidth(), isLastColumn);
    const deltas = determine(widths, index, clampedStep, tableSize, resizing);
    const newWidths = map$1(deltas, (dx, i) => dx + widths[i]);
    recalculateAndApply(warehouse, newWidths, tableSize);
    resizing.resizeTable(tableSize.adjustTableWidth, clampedStep, isLastColumn);
  };
  const adjustHeight = (table, delta, index) => {
    const warehouse = Warehouse.fromTable(table);
    const heights = getPixelHeights(warehouse, table);
    const newHeights = map$1(heights, (dy, i) => index === i ? Math.max(delta + dy, minHeight()) : dy);
    const newRowSizes = matchRowHeight(warehouse, newHeights);
    each$2(newRowSizes, row => {
      setHeight(row.element, row.height);
    });
    each$2(Warehouse.justCells(warehouse), cell => {
      removeHeight(cell.element);
    });
    const total = sumUp(newHeights);
    setHeight(table, total);
  };
  const adjustAndRedistributeWidths$1 = (_table, list, details, tableSize, resizeBehaviour) => {
    const warehouse = Warehouse.generate(list);
    const sizes = tableSize.getWidths(warehouse, tableSize);
    const tablePixelWidth = tableSize.pixelWidth();
    const {
      newSizes,
      delta
    } = resizeBehaviour.calcRedestributedWidths(sizes, tablePixelWidth, details.pixelDelta, tableSize.isRelative);
    recalculateAndApply(warehouse, newSizes, tableSize);
    tableSize.adjustTableWidth(delta);
  };
  const adjustWidthTo = (_table, list, _info, tableSize) => {
    const warehouse = Warehouse.generate(list);
    const widths = tableSize.getWidths(warehouse, tableSize);
    recalculateAndApply(warehouse, widths, tableSize);
  };

  const uniqueColumns = details => {
    const uniqueCheck = (rest, detail) => {
      const columnExists = exists(rest, currentDetail => currentDetail.column === detail.column);
      return columnExists ? rest : rest.concat([detail]);
    };
    return foldl(details, uniqueCheck, []).sort((detailA, detailB) => detailA.column - detailB.column);
  };

  const isCol = isTag("col");
  const isColgroup = isTag("colgroup");
  const isRow$1 = element => name(element) === "tr" || isColgroup(element);
  const elementToData = element => {
    const colspan = getAttrValue(element, "colspan", 1);
    const rowspan = getAttrValue(element, "rowspan", 1);
    return {
      element,
      colspan,
      rowspan
    };
  };
  const modification = (generators, toData = elementToData) => {
    const nuCell = data => isCol(data.element) ? generators.col(data) : generators.cell(data);
    const nuRow = data => isColgroup(data.element) ? generators.colgroup(data) : generators.row(data);
    const add = element => {
      if (isRow$1(element)) {
        return nuRow({ element });
      } else {
        const cell = element;
        const replacement = nuCell(toData(cell));
        recent = Optional.some({
          item: cell,
          replacement
        });
        return replacement;
      }
    };
    let recent = Optional.none();
    const getOrInit = (element, comparator) => {
      return recent.fold(() => {
        return add(element);
      }, p => {
        return comparator(element, p.item) ? p.replacement : add(element);
      });
    };
    return { getOrInit };
  };
  const transform$1 = tag => {
    return generators => {
      const list = [];
      const find = (element, comparator) => {
        return find$1(list, x => {
          return comparator(x.item, element);
        });
      };
      const makeNew = element => {
        const attrs = tag === "td" ? { scope: null } : {};
        const cell = generators.replace(element, tag, attrs);
        list.push({
          item: element,
          sub: cell
        });
        return cell;
      };
      const replaceOrInit = (element, comparator) => {
        if (isRow$1(element) || isCol(element)) {
          return element;
        } else {
          const cell = element;
          return find(cell, comparator).fold(() => {
            return makeNew(cell);
          }, p => {
            return comparator(element, p.item) ? p.sub : makeNew(cell);
          });
        }
      };
      return { replaceOrInit };
    };
  };
  const getScopeAttribute = cell => getOpt(cell, "scope").map(attribute => attribute.substr(0, 3));
  const merging = generators => {
    const unmerge = cell => {
      const scope = getScopeAttribute(cell);
      scope.each(attribute => set$2(cell, "scope", attribute));
      return () => {
        const raw = generators.cell({
          element: cell,
          colspan: 1,
          rowspan: 1
        });
        remove$5(raw, "width");
        remove$5(cell, "width");
        scope.each(attribute => set$2(raw, "scope", attribute));
        return raw;
      };
    };
    const merge = cells => {
      const getScopeProperty = () => {
        const stringAttributes = cat(map$1(cells, getScopeAttribute));
        if (stringAttributes.length === 0) {
          return Optional.none();
        } else {
          const baseScope = stringAttributes[0];
          const scopes = [
            "row",
            "col"
          ];
          const isMixed = exists(stringAttributes, attribute => {
            return attribute !== baseScope && contains$2(scopes, attribute);
          });
          return isMixed ? Optional.none() : Optional.from(baseScope);
        }
      };
      remove$5(cells[0], "width");
      getScopeProperty().fold(() => remove$7(cells[0], "scope"), attribute => set$2(cells[0], "scope", attribute + "group"));
      return constant(cells[0]);
    };
    return {
      unmerge,
      merge
    };
  };
  const Generators = {
    modification,
    transform: transform$1,
    merging
  };

  const blockList = [
    "body",
    "p",
    "div",
    "article",
    "aside",
    "figcaption",
    "figure",
    "footer",
    "header",
    "nav",
    "section",
    "ol",
    "ul",
    "table",
    "thead",
    "tfoot",
    "tbody",
    "caption",
    "tr",
    "td",
    "th",
    "h1",
    "h2",
    "h3",
    "h4",
    "h5",
    "h6",
    "blockquote",
    "pre",
    "address"
  ];
  const isList$1 = (universe, item) => {
    const tagName = universe.property().name(item);
    return contains$2([
      "ol",
      "ul"
    ], tagName);
  };
  const isBlock$1 = (universe, item) => {
    const tagName = universe.property().name(item);
    return contains$2(blockList, tagName);
  };
  const isEmptyTag$1 = (universe, item) => {
    return contains$2([
      "br",
      "img",
      "hr",
      "input"
    ], universe.property().name(item));
  };

  const universe$1 = DomUniverse();
  const isBlock = element => {
    return isBlock$1(universe$1, element);
  };
  const isList = element => {
    return isList$1(universe$1, element);
  };
  const isEmptyTag = element => {
    return isEmptyTag$1(universe$1, element);
  };

  const merge = cells => {
    const isBr = isTag("br");
    const advancedBr = children => {
      return forall(children, c => {
        return isBr(c) || isText(c) && get$6(c).trim().length === 0;
      });
    };
    const isListItem = el => {
      return name(el) === "li" || ancestor$2(el, isList).isSome();
    };
    const siblingIsBlock = el => {
      return nextSibling(el).map(rightSibling => {
        if (isBlock(rightSibling)) {
          return true;
        }
        if (isEmptyTag(rightSibling)) {
          return name(rightSibling) === "img" ? false : true;
        }
        return false;
      }).getOr(false);
    };
    const markCell = cell => {
      return last$1(cell).bind(rightEdge => {
        const rightSiblingIsBlock = siblingIsBlock(rightEdge);
        return parent(rightEdge).map(parent => {
          return rightSiblingIsBlock === true || isListItem(parent) || isBr(rightEdge) || isBlock(parent) && !eq$1(cell, parent) ? [] : [SugarElement.fromTag("br")];
        });
      }).getOr([]);
    };
    const markContent = () => {
      const content = bind$2(cells, cell => {
        const children = children$2(cell);
        return advancedBr(children) ? [] : children.concat(markCell(cell));
      });
      return content.length === 0 ? [SugarElement.fromTag("br")] : content;
    };
    const contents = markContent();
    empty(cells[0]);
    append(cells[0], contents);
  };

  const isEditable = elem => isEditable$1(elem, true);
  const prune = table => {
    const cells = cells$1(table);
    if (cells.length === 0) {
      remove$6(table);
    }
  };
  const outcome = (grid, cursor) => ({
    grid,
    cursor
  });
  const findEditableCursorPosition = rows => findMap(rows, row => findMap(row.cells, cell => {
    const elem = cell.element;
    return someIf(isEditable(elem), elem);
  }));
  const elementFromGrid = (grid, row, column) => {
    var _a, _b;
    const rows = extractGridDetails(grid).rows;
    return Optional.from((_b = (_a = rows[row]) === null || _a === void 0 ? void 0 : _a.cells[column]) === null || _b === void 0 ? void 0 : _b.element).filter(isEditable).orThunk(() => findEditableCursorPosition(rows));
  };
  const bundle = (grid, row, column) => {
    const cursorElement = elementFromGrid(grid, row, column);
    return outcome(grid, cursorElement);
  };
  const uniqueRows = details => {
    const rowCompilation = (rest, detail) => {
      const rowExists = exists(rest, currentDetail => currentDetail.row === detail.row);
      return rowExists ? rest : rest.concat([detail]);
    };
    return foldl(details, rowCompilation, []).sort((detailA, detailB) => detailA.row - detailB.row);
  };
  const opInsertRowsBefore = (grid, details, comparator, genWrappers) => {
    const targetIndex = details[0].row;
    const rows = uniqueRows(details);
    const newGrid = foldr(rows, (acc, row) => {
      const newG = insertRowAt(acc.grid, targetIndex, row.row + acc.delta, comparator, genWrappers.getOrInit);
      return {
        grid: newG,
        delta: acc.delta + 1
      };
    }, {
      grid,
      delta: 0
    }).grid;
    return bundle(newGrid, targetIndex, details[0].column);
  };
  const opInsertRowsAfter = (grid, details, comparator, genWrappers) => {
    const rows = uniqueRows(details);
    const target = rows[rows.length - 1];
    const targetIndex = target.row + target.rowspan;
    const newGrid = foldr(rows, (newG, row) => {
      return insertRowAt(newG, targetIndex, row.row, comparator, genWrappers.getOrInit);
    }, grid);
    return bundle(newGrid, targetIndex, details[0].column);
  };
  const opInsertColumnsBefore = (grid, extractDetail, comparator, genWrappers) => {
    const details = extractDetail.details;
    const columns = uniqueColumns(details);
    const targetIndex = columns[0].column;
    const newGrid = foldr(columns, (acc, col) => {
      const newG = insertColumnAt(acc.grid, targetIndex, col.column + acc.delta, comparator, genWrappers.getOrInit);
      return {
        grid: newG,
        delta: acc.delta + 1
      };
    }, {
      grid,
      delta: 0
    }).grid;
    return bundle(newGrid, details[0].row, targetIndex);
  };
  const opInsertColumnsAfter = (grid, extractDetail, comparator, genWrappers) => {
    const details = extractDetail.details;
    const target = details[details.length - 1];
    const targetIndex = target.column + target.colspan;
    const columns = uniqueColumns(details);
    const newGrid = foldr(columns, (newG, col) => {
      return insertColumnAt(newG, targetIndex, col.column, comparator, genWrappers.getOrInit);
    }, grid);
    return bundle(newGrid, details[0].row, targetIndex);
  };
  const opMakeColumnsHeader = (initialGrid, details, comparator, genWrappers) => {
    const columns = uniqueColumns(details);
    const columnIndexes = map$1(columns, detail => detail.column);
    const newGrid = replaceColumns(initialGrid, columnIndexes, true, comparator, genWrappers.replaceOrInit);
    return bundle(newGrid, details[0].row, details[0].column);
  };
  const opMakeCellsHeader = (initialGrid, details, comparator, genWrappers) => {
    const newGrid = replaceCells(initialGrid, details, comparator, genWrappers.replaceOrInit);
    return bundle(newGrid, details[0].row, details[0].column);
  };
  const opUnmakeColumnsHeader = (initialGrid, details, comparator, genWrappers) => {
    const columns = uniqueColumns(details);
    const columnIndexes = map$1(columns, detail => detail.column);
    const newGrid = replaceColumns(initialGrid, columnIndexes, false, comparator, genWrappers.replaceOrInit);
    return bundle(newGrid, details[0].row, details[0].column);
  };
  const opUnmakeCellsHeader = (initialGrid, details, comparator, genWrappers) => {
    const newGrid = replaceCells(initialGrid, details, comparator, genWrappers.replaceOrInit);
    return bundle(newGrid, details[0].row, details[0].column);
  };
  const makeRowsSection = (section, applyScope) => (initialGrid, details, comparator, genWrappers, tableSection) => {
    const rows = uniqueRows(details);
    const rowIndexes = map$1(rows, detail => detail.row);
    const newGrid = replaceRows(initialGrid, rowIndexes, section, applyScope, comparator, genWrappers.replaceOrInit, tableSection);
    return bundle(newGrid, details[0].row, details[0].column);
  };
  const opMakeRowsHeader = makeRowsSection("thead", true);
  const opMakeRowsBody = makeRowsSection("tbody", false);
  const opMakeRowsFooter = makeRowsSection("tfoot", false);
  const opEraseColumns = (grid, extractDetail, _comparator, _genWrappers) => {
    const columns = uniqueColumns(extractDetail.details);
    const newGrid = deleteColumnsAt(grid, map$1(columns, column => column.column));
    const maxColIndex = newGrid.length > 0 ? newGrid[0].cells.length - 1 : 0;
    return bundle(newGrid, columns[0].row, Math.min(columns[0].column, maxColIndex));
  };
  const opEraseRows = (grid, details, _comparator, _genWrappers) => {
    const rows = uniqueRows(details);
    const newGrid = deleteRowsAt(grid, rows[0].row, rows[rows.length - 1].row);
    const maxRowIndex = Math.max(extractGridDetails(newGrid).rows.length - 1, 0);
    return bundle(newGrid, Math.min(details[0].row, maxRowIndex), details[0].column);
  };
  const opMergeCells = (grid, mergable, comparator, genWrappers) => {
    const cells = mergable.cells;
    merge(cells);
    const newGrid = merge$2(grid, mergable.bounds, comparator, genWrappers.merge(cells));
    return outcome(newGrid, Optional.from(cells[0]));
  };
  const opUnmergeCells = (grid, unmergable, comparator, genWrappers) => {
    const unmerge$1 = (b, cell) => unmerge(b, cell, comparator, genWrappers.unmerge(cell));
    const newGrid = foldr(unmergable, unmerge$1, grid);
    return outcome(newGrid, Optional.from(unmergable[0]));
  };
  const opPasteCells = (grid, pasteDetails, comparator, _genWrappers) => {
    const gridify = (table, generators) => {
      const wh = Warehouse.fromTable(table);
      return toGrid(wh, generators, true);
    };
    const gridB = gridify(pasteDetails.clipboard, pasteDetails.generators);
    const startAddress = address(pasteDetails.row, pasteDetails.column);
    const mergedGrid = merge$1(startAddress, grid, gridB, pasteDetails.generators, comparator);
    return mergedGrid.fold(() => outcome(grid, Optional.some(pasteDetails.element)), newGrid => {
      return bundle(newGrid, pasteDetails.row, pasteDetails.column);
    });
  };
  const gridifyRows = (rows, generators, context) => {
    const pasteDetails = fromPastedRows(rows, context.section);
    const wh = Warehouse.generate(pasteDetails);
    return toGrid(wh, generators, true);
  };
  const opPasteColsBefore = (grid, pasteDetails, comparator, _genWrappers) => {
    const rows = extractGridDetails(grid).rows;
    const index = pasteDetails.cells[0].column;
    const context = rows[pasteDetails.cells[0].row];
    const gridB = gridifyRows(pasteDetails.clipboard, pasteDetails.generators, context);
    const mergedGrid = insertCols(index, grid, gridB, pasteDetails.generators, comparator);
    return bundle(mergedGrid, pasteDetails.cells[0].row, pasteDetails.cells[0].column);
  };
  const opPasteColsAfter = (grid, pasteDetails, comparator, _genWrappers) => {
    const rows = extractGridDetails(grid).rows;
    const index = pasteDetails.cells[pasteDetails.cells.length - 1].column + pasteDetails.cells[pasteDetails.cells.length - 1].colspan;
    const context = rows[pasteDetails.cells[0].row];
    const gridB = gridifyRows(pasteDetails.clipboard, pasteDetails.generators, context);
    const mergedGrid = insertCols(index, grid, gridB, pasteDetails.generators, comparator);
    return bundle(mergedGrid, pasteDetails.cells[0].row, index);
  };
  const opPasteRowsBefore = (grid, pasteDetails, comparator, _genWrappers) => {
    const rows = extractGridDetails(grid).rows;
    const index = pasteDetails.cells[0].row;
    const context = rows[index];
    const gridB = gridifyRows(pasteDetails.clipboard, pasteDetails.generators, context);
    const mergedGrid = insertRows(index, grid, gridB, pasteDetails.generators, comparator);
    return bundle(mergedGrid, pasteDetails.cells[0].row, pasteDetails.cells[0].column);
  };
  const opPasteRowsAfter = (grid, pasteDetails, comparator, _genWrappers) => {
    const rows = extractGridDetails(grid).rows;
    const index = pasteDetails.cells[pasteDetails.cells.length - 1].row + pasteDetails.cells[pasteDetails.cells.length - 1].rowspan;
    const context = rows[pasteDetails.cells[0].row];
    const gridB = gridifyRows(pasteDetails.clipboard, pasteDetails.generators, context);
    const mergedGrid = insertRows(index, grid, gridB, pasteDetails.generators, comparator);
    return bundle(mergedGrid, index, pasteDetails.cells[0].column);
  };
  const opGetColumnsType = (table, target) => {
    const house = Warehouse.fromTable(table);
    const details = onCells(house, target);
    return details.bind(selectedCells => {
      const lastSelectedCell = selectedCells[selectedCells.length - 1];
      const minColRange = selectedCells[0].column;
      const maxColRange = lastSelectedCell.column + lastSelectedCell.colspan;
      const selectedColumnCells = flatten(map$1(house.all, row => filter$2(row.cells, cell => cell.column >= minColRange && cell.column < maxColRange)));
      return findCommonCellType(selectedColumnCells);
    }).getOr("");
  };
  const opGetCellsType = (table, target) => {
    const house = Warehouse.fromTable(table);
    const details = onCells(house, target);
    return details.bind(findCommonCellType).getOr("");
  };
  const opGetRowsType = (table, target) => {
    const house = Warehouse.fromTable(table);
    const details = onCells(house, target);
    return details.bind(selectedCells => {
      const lastSelectedCell = selectedCells[selectedCells.length - 1];
      const minRowRange = selectedCells[0].row;
      const maxRowRange = lastSelectedCell.row + lastSelectedCell.rowspan;
      const selectedRows = house.all.slice(minRowRange, maxRowRange);
      return findCommonRowType(selectedRows);
    }).getOr("");
  };
  const resize = (table, list, details, behaviours) => adjustWidthTo(table, list, details, behaviours.sizing);
  const adjustAndRedistributeWidths = (table, list, details, behaviours) => adjustAndRedistributeWidths$1(table, list, details, behaviours.sizing, behaviours.resize);
  const firstColumnIsLocked = (_warehouse, details) => exists(details, detail => detail.column === 0 && detail.isLocked);
  const lastColumnIsLocked = (warehouse, details) => exists(details, detail => detail.column + detail.colspan >= warehouse.grid.columns && detail.isLocked);
  const getColumnsWidth = (warehouse, details) => {
    const columns$1 = columns(warehouse);
    const uniqueCols = uniqueColumns(details);
    return foldl(uniqueCols, (acc, detail) => {
      const column = columns$1[detail.column];
      const colWidth = column.map(getOuter$2).getOr(0);
      return acc + colWidth;
    }, 0);
  };
  const insertColumnsExtractor = before => (warehouse, target) => onCells(warehouse, target).filter(details => {
    const checkLocked = before ? firstColumnIsLocked : lastColumnIsLocked;
    return !checkLocked(warehouse, details);
  }).map(details => ({
    details,
    pixelDelta: getColumnsWidth(warehouse, details)
  }));
  const eraseColumnsExtractor = (warehouse, target) => onUnlockedCells(warehouse, target).map(details => ({
    details,
    pixelDelta: -getColumnsWidth(warehouse, details)
  }));
  const pasteColumnsExtractor = before => (warehouse, target) => onPasteByEditor(warehouse, target).filter(details => {
    const checkLocked = before ? firstColumnIsLocked : lastColumnIsLocked;
    return !checkLocked(warehouse, details.cells);
  });
  const headerCellGenerator = Generators.transform("th");
  const bodyCellGenerator = Generators.transform("td");
  const insertRowsBefore = run(opInsertRowsBefore, onCells, noop, noop, Generators.modification);
  const insertRowsAfter = run(opInsertRowsAfter, onCells, noop, noop, Generators.modification);
  const insertColumnsBefore = run(opInsertColumnsBefore, insertColumnsExtractor(true), adjustAndRedistributeWidths, noop, Generators.modification);
  const insertColumnsAfter = run(opInsertColumnsAfter, insertColumnsExtractor(false), adjustAndRedistributeWidths, noop, Generators.modification);
  const eraseColumns = run(opEraseColumns, eraseColumnsExtractor, adjustAndRedistributeWidths, prune, Generators.modification);
  const eraseRows = run(opEraseRows, onCells, noop, prune, Generators.modification);
  const makeColumnsHeader = run(opMakeColumnsHeader, onUnlockedCells, noop, noop, headerCellGenerator);
  const unmakeColumnsHeader = run(opUnmakeColumnsHeader, onUnlockedCells, noop, noop, bodyCellGenerator);
  const makeRowsHeader = run(opMakeRowsHeader, onCells, noop, noop, headerCellGenerator);
  const makeRowsBody = run(opMakeRowsBody, onCells, noop, noop, bodyCellGenerator);
  const makeRowsFooter = run(opMakeRowsFooter, onCells, noop, noop, bodyCellGenerator);
  const makeCellsHeader = run(opMakeCellsHeader, onUnlockedCells, noop, noop, headerCellGenerator);
  const unmakeCellsHeader = run(opUnmakeCellsHeader, onUnlockedCells, noop, noop, bodyCellGenerator);
  const mergeCells = run(opMergeCells, onUnlockedMergable, resize, noop, Generators.merging);
  const unmergeCells = run(opUnmergeCells, onUnlockedUnmergable, resize, noop, Generators.merging);
  const pasteCells = run(opPasteCells, onPaste, resize, noop, Generators.modification);
  const pasteColsBefore = run(opPasteColsBefore, pasteColumnsExtractor(true), noop, noop, Generators.modification);
  const pasteColsAfter = run(opPasteColsAfter, pasteColumnsExtractor(false), noop, noop, Generators.modification);
  const pasteRowsBefore = run(opPasteRowsBefore, onPasteByEditor, noop, noop, Generators.modification);
  const pasteRowsAfter = run(opPasteRowsAfter, onPasteByEditor, noop, noop, Generators.modification);
  const getColumnsType = opGetColumnsType;
  const getCellsType = opGetCellsType;
  const getRowsType = opGetRowsType;

  const fireNewRow = (editor, row) => editor.dispatch("NewRow", { node: row });
  const fireNewCell = (editor, cell) => editor.dispatch("NewCell", { node: cell });
  const fireTableModified = (editor, table, data) => {
    editor.dispatch("TableModified", {
      ...data,
      table
    });
  };
  const fireTableSelectionChange = (editor, cells, start, finish, otherCells) => {
    editor.dispatch("TableSelectionChange", {
      cells,
      start,
      finish,
      otherCells
    });
  };
  const fireTableSelectionClear = editor => {
    editor.dispatch("TableSelectionClear");
  };
  const fireObjectResizeStart = (editor, target, width, height, origin) => {
    editor.dispatch("ObjectResizeStart", {
      target,
      width,
      height,
      origin
    });
  };
  const fireObjectResized = (editor, target, width, height, origin) => {
    editor.dispatch("ObjectResized", {
      target,
      width,
      height,
      origin
    });
  };
  const styleModified = {
    structure: false,
    style: true
  };
  const structureModified = {
    structure: true,
    style: false
  };
  const styleAndStructureModified = {
    structure: true,
    style: true
  };

  const get$5 = (editor, table) => {
    if (isTablePercentagesForced(editor)) {
      return TableSize.percentageSize(table);
    } else if (isTablePixelsForced(editor)) {
      return TableSize.pixelSize(table);
    } else {
      return TableSize.getTableSize(table);
    }
  };

  const TableActions = (editor, resizeHandler, cellSelectionHandler) => {
    const isTableBody = editor => name(getBody(editor)) === "table";
    const lastRowGuard = table => !isTableBody(editor) || getGridSize(table).rows > 1;
    const lastColumnGuard = table => !isTableBody(editor) || getGridSize(table).columns > 1;
    const cloneFormats = getTableCloneElements(editor);
    const colMutationOp = isResizeTableColumnResizing(editor) ? noop : halve;
    const getTableSectionType = table => {
      switch (getTableHeaderType(editor)) {
        case "section":
          return TableSection.section();
        case "sectionCells":
          return TableSection.sectionCells();
        case "cells":
          return TableSection.cells();
        default:
          return TableSection.getTableSectionType(table, "section");
      }
    };
    const setSelectionFromAction = (table, result) => result.cursor.fold(() => {
      const cells = cells$1(table);
      return head(cells).filter(inBody).map(firstCell => {
        cellSelectionHandler.clearSelectedCells(table.dom);
        const rng = editor.dom.createRng();
        rng.selectNode(firstCell.dom);
        editor.selection.setRng(rng);
        set$2(firstCell, "data-mce-selected", "1");
        return rng;
      });
    }, cell => {
      const des = freefallRtl(cell);
      const rng = editor.dom.createRng();
      rng.setStart(des.element.dom, des.offset);
      rng.setEnd(des.element.dom, des.offset);
      editor.selection.setRng(rng);
      cellSelectionHandler.clearSelectedCells(table.dom);
      return Optional.some(rng);
    });
    const execute = (operation, guard, mutate, effect) => (table, target, noEvents = false) => {
      removeDataStyle(table);
      const doc = SugarElement.fromDom(editor.getDoc());
      const generators = cellOperations(mutate, doc, cloneFormats);
      const behaviours = {
        sizing: get$5(editor, table),
        resize: isResizeTableColumnResizing(editor) ? resizeTable() : preserveTable(),
        section: getTableSectionType(table)
      };
      return guard(table) ? operation(table, target, generators, behaviours).bind(result => {
        resizeHandler.refresh(table.dom);
        each$2(result.newRows, row => {
          fireNewRow(editor, row.dom);
        });
        each$2(result.newCells, cell => {
          fireNewCell(editor, cell.dom);
        });
        const range = setSelectionFromAction(table, result);
        if (inBody(table)) {
          removeDataStyle(table);
          if (!noEvents) {
            fireTableModified(editor, table.dom, effect);
          }
        }
        return range.map(rng => ({
          rng,
          effect
        }));
      }) : Optional.none();
    };
    const deleteRow = execute(eraseRows, lastRowGuard, noop, structureModified);
    const deleteColumn = execute(eraseColumns, lastColumnGuard, noop, structureModified);
    const insertRowsBefore$1 = execute(insertRowsBefore, always, noop, structureModified);
    const insertRowsAfter$1 = execute(insertRowsAfter, always, noop, structureModified);
    const insertColumnsBefore$1 = execute(insertColumnsBefore, always, colMutationOp, structureModified);
    const insertColumnsAfter$1 = execute(insertColumnsAfter, always, colMutationOp, structureModified);
    const mergeCells$1 = execute(mergeCells, always, noop, structureModified);
    const unmergeCells$1 = execute(unmergeCells, always, noop, structureModified);
    const pasteColsBefore$1 = execute(pasteColsBefore, always, noop, structureModified);
    const pasteColsAfter$1 = execute(pasteColsAfter, always, noop, structureModified);
    const pasteRowsBefore$1 = execute(pasteRowsBefore, always, noop, structureModified);
    const pasteRowsAfter$1 = execute(pasteRowsAfter, always, noop, structureModified);
    const pasteCells$1 = execute(pasteCells, always, noop, styleAndStructureModified);
    const makeCellsHeader$1 = execute(makeCellsHeader, always, noop, structureModified);
    const unmakeCellsHeader$1 = execute(unmakeCellsHeader, always, noop, structureModified);
    const makeColumnsHeader$1 = execute(makeColumnsHeader, always, noop, structureModified);
    const unmakeColumnsHeader$1 = execute(unmakeColumnsHeader, always, noop, structureModified);
    const makeRowsHeader$1 = execute(makeRowsHeader, always, noop, structureModified);
    const makeRowsBody$1 = execute(makeRowsBody, always, noop, structureModified);
    const makeRowsFooter$1 = execute(makeRowsFooter, always, noop, structureModified);
    const getTableCellType = getCellsType;
    const getTableColType = getColumnsType;
    const getTableRowType = getRowsType;
    return {
      deleteRow,
      deleteColumn,
      insertRowsBefore: insertRowsBefore$1,
      insertRowsAfter: insertRowsAfter$1,
      insertColumnsBefore: insertColumnsBefore$1,
      insertColumnsAfter: insertColumnsAfter$1,
      mergeCells: mergeCells$1,
      unmergeCells: unmergeCells$1,
      pasteColsBefore: pasteColsBefore$1,
      pasteColsAfter: pasteColsAfter$1,
      pasteRowsBefore: pasteRowsBefore$1,
      pasteRowsAfter: pasteRowsAfter$1,
      pasteCells: pasteCells$1,
      makeCellsHeader: makeCellsHeader$1,
      unmakeCellsHeader: unmakeCellsHeader$1,
      makeColumnsHeader: makeColumnsHeader$1,
      unmakeColumnsHeader: unmakeColumnsHeader$1,
      makeRowsHeader: makeRowsHeader$1,
      makeRowsBody: makeRowsBody$1,
      makeRowsFooter: makeRowsFooter$1,
      getTableRowType,
      getTableCellType,
      getTableColType
    };
  };

  const constrainSpan = (element, property, value) => {
    const currentColspan = getAttrValue(element, property, 1);
    if (value === 1 || currentColspan <= 1) {
      remove$7(element, property);
    } else {
      set$2(element, property, Math.min(value, currentColspan));
    }
  };
  const isColInRange = (minColRange, maxColRange) => cell => {
    const endCol = cell.column + cell.colspan - 1;
    const startCol = cell.column;
    return endCol >= minColRange && startCol < maxColRange;
  };
  const generateColGroup = (house, minColRange, maxColRange) => {
    if (Warehouse.hasColumns(house)) {
      const colsToCopy = filter$2(Warehouse.justColumns(house), isColInRange(minColRange, maxColRange));
      const copiedCols = map$1(colsToCopy, c => {
        const clonedCol = deep(c.element);
        constrainSpan(clonedCol, "span", maxColRange - minColRange);
        return clonedCol;
      });
      const fakeColgroup = SugarElement.fromTag("colgroup");
      append(fakeColgroup, copiedCols);
      return [fakeColgroup];
    } else {
      return [];
    }
  };
  const generateRows = (house, minColRange, maxColRange) => map$1(house.all, row => {
    const cellsToCopy = filter$2(row.cells, isColInRange(minColRange, maxColRange));
    const copiedCells = map$1(cellsToCopy, cell => {
      const clonedCell = deep(cell.element);
      constrainSpan(clonedCell, "colspan", maxColRange - minColRange);
      return clonedCell;
    });
    const fakeTR = SugarElement.fromTag("tr");
    append(fakeTR, copiedCells);
    return fakeTR;
  });
  const copyCols = (table, target) => {
    const house = Warehouse.fromTable(table);
    const details = onUnlockedCells(house, target);
    return details.map(selectedCells => {
      const lastSelectedCell = selectedCells[selectedCells.length - 1];
      const minColRange = selectedCells[0].column;
      const maxColRange = lastSelectedCell.column + lastSelectedCell.colspan;
      const fakeColGroups = generateColGroup(house, minColRange, maxColRange);
      const fakeRows = generateRows(house, minColRange, maxColRange);
      return [
        ...fakeColGroups,
        ...fakeRows
      ];
    });
  };

  const copyRows = (table, target, generators) => {
    const warehouse = Warehouse.fromTable(table);
    const details = onCells(warehouse, target);
    return details.bind(selectedCells => {
      const grid = toGrid(warehouse, generators, false);
      const rows = extractGridDetails(grid).rows;
      const slicedGrid = rows.slice(selectedCells[0].row, selectedCells[selectedCells.length - 1].row + selectedCells[selectedCells.length - 1].rowspan);
      const filteredGrid = bind$2(slicedGrid, row => {
        const newCells = filter$2(row.cells, cell => !cell.isLocked);
        return newCells.length > 0 ? [{
          ...row,
          cells: newCells
        }] : [];
      });
      const slicedDetails = toDetailList(filteredGrid);
      return someIf(slicedDetails.length > 0, slicedDetails);
    }).map(slicedDetails => copy(slicedDetails));
  };

  const adt$5 = Adt.generate([
    { invalid: ["raw"] },
    { pixels: ["value"] },
    { percent: ["value"] }
  ]);
  const validateFor = (suffix, type, value) => {
    const rawAmount = value.substring(0, value.length - suffix.length);
    const amount = parseFloat(rawAmount);
    return rawAmount === amount.toString() ? type(amount) : adt$5.invalid(value);
  };
  const from = value => {
    if (endsWith(value, "%")) {
      return validateFor("%", adt$5.percent, value);
    }
    if (endsWith(value, "px")) {
      return validateFor("px", adt$5.pixels, value);
    }
    return adt$5.invalid(value);
  };
  const Size = {
    ...adt$5,
    from
  };

  const redistributeToPercent = (widths, totalWidth) => {
    return map$1(widths, w => {
      const colType = Size.from(w);
      return colType.fold(() => {
        return w;
      }, px => {
        const ratio = px / totalWidth * 100;
        return ratio + "%";
      }, pc => {
        return pc + "%";
      });
    });
  };
  const redistributeToPx = (widths, totalWidth, newTotalWidth) => {
    const scale = newTotalWidth / totalWidth;
    return map$1(widths, w => {
      const colType = Size.from(w);
      return colType.fold(() => {
        return w;
      }, px => {
        return px * scale + "px";
      }, pc => {
        return pc / 100 * newTotalWidth + "px";
      });
    });
  };
  const redistributeEmpty = (newWidthType, columns) => {
    const f = newWidthType.fold(() => constant(""), pixels => {
      const num = pixels / columns;
      return constant(num + "px");
    }, () => {
      const num = 100 / columns;
      return constant(num + "%");
    });
    return range$1(columns, f);
  };
  const redistributeValues = (newWidthType, widths, totalWidth) => {
    return newWidthType.fold(() => {
      return widths;
    }, px => {
      return redistributeToPx(widths, totalWidth, px);
    }, _pc => {
      return redistributeToPercent(widths, totalWidth);
    });
  };
  const redistribute$1 = (widths, totalWidth, newWidth) => {
    const newType = Size.from(newWidth);
    const floats = forall(widths, s => {
      return s === "0px";
    }) ? redistributeEmpty(newType, widths.length) : redistributeValues(newType, widths, totalWidth);
    return normalize(floats);
  };
  const sum = (values, fallback) => {
    if (values.length === 0) {
      return fallback;
    }
    return foldr(values, (rest, v) => {
      return Size.from(v).fold(constant(0), identity, identity) + rest;
    }, 0);
  };
  const roundDown = (num, unit) => {
    const floored = Math.floor(num);
    return {
      value: floored + unit,
      remainder: num - floored
    };
  };
  const add$3 = (value, amount) => {
    return Size.from(value).fold(constant(value), px => {
      return px + amount + "px";
    }, pc => {
      return pc + amount + "%";
    });
  };
  const normalize = values => {
    if (values.length === 0) {
      return values;
    }
    const scan = foldr(values, (rest, value) => {
      const info = Size.from(value).fold(() => ({
        value,
        remainder: 0
      }), num => roundDown(num, "px"), num => ({
        value: num + "%",
        remainder: 0
      }));
      return {
        output: [info.value].concat(rest.output),
        remainder: rest.remainder + info.remainder
      };
    }, {
      output: [],
      remainder: 0
    });
    const r = scan.output;
    return r.slice(0, r.length - 1).concat([add$3(r[r.length - 1], Math.round(scan.remainder))]);
  };
  const validate = Size.from;

  const redistributeToW = (newWidths, cells, unit) => {
    each$2(cells, cell => {
      const widths = newWidths.slice(cell.column, cell.colspan + cell.column);
      const w = sum(widths, minWidth());
      set$1(cell.element, "width", w + unit);
    });
  };
  const redistributeToColumns = (newWidths, columns, unit) => {
    each$2(columns, (column, index) => {
      const width = sum([newWidths[index]], minWidth());
      set$1(column.element, "width", width + unit);
    });
  };
  const redistributeToH = (newHeights, rows, cells) => {
    each$2(cells, cell => {
      remove$5(cell.element, "height");
    });
    each$2(rows, (row, i) => {
      set$1(row.element, "height", newHeights[i]);
    });
  };
  const getUnit = newSize => {
    return validate(newSize).fold(constant("px"), constant("px"), constant("%"));
  };
  const redistribute = (table, optWidth, optHeight) => {
    const warehouse = Warehouse.fromTable(table);
    const rows = warehouse.all;
    const cells = Warehouse.justCells(warehouse);
    const columns = Warehouse.justColumns(warehouse);
    optWidth.each(newWidth => {
      const widthUnit = getUnit(newWidth);
      const totalWidth = get$9(table);
      const oldWidths = getRawWidths(warehouse, table);
      const nuWidths = redistribute$1(oldWidths, totalWidth, newWidth);
      if (Warehouse.hasColumns(warehouse)) {
        redistributeToColumns(nuWidths, columns, widthUnit);
      } else {
        redistributeToW(nuWidths, cells, widthUnit);
      }
      set$1(table, "width", newWidth);
    });
    optHeight.each(newHeight => {
      const totalHeight = get$8(table);
      const oldHeights = getRawHeights(warehouse, table);
      const nuHeights = redistribute$1(oldHeights, totalHeight, newHeight);
      redistributeToH(nuHeights, rows, cells);
      set$1(table, "height", newHeight);
    });
  };
  const isPercentSizing = isPercentSizing$1;
  const isPixelSizing = isPixelSizing$1;
  const isNoneSizing = isNoneSizing$1;

  const cleanupLegacyAttributes = element => {
    remove$7(element, "width");
    remove$7(element, "height");
  };
  const convertToPercentSizeWidth = table => {
    const newWidth = getPercentTableWidth(table);
    redistribute(table, Optional.some(newWidth), Optional.none());
    cleanupLegacyAttributes(table);
  };
  const convertToPixelSizeWidth = table => {
    const newWidth = getPixelTableWidth(table);
    redistribute(table, Optional.some(newWidth), Optional.none());
    cleanupLegacyAttributes(table);
  };
  const convertToPixelSizeHeight = table => {
    const newHeight = getPixelTableHeight(table);
    redistribute(table, Optional.none(), Optional.some(newHeight));
    cleanupLegacyAttributes(table);
  };
  const convertToNoneSizeWidth = table => {
    remove$5(table, "width");
    const columns = columns$1(table);
    const rowElements = columns.length > 0 ? columns : cells$1(table);
    each$2(rowElements, cell => {
      remove$5(cell, "width");
      cleanupLegacyAttributes(cell);
    });
    cleanupLegacyAttributes(table);
  };

  const DefaultRenderOptions = {
    styles: {
      "border-collapse": "collapse",
      "width": "100%"
    },
    attributes: { border: "1" },
    colGroups: false
  };
  const tableHeaderCell = () => SugarElement.fromTag("th");
  const tableCell = () => SugarElement.fromTag("td");
  const tableColumn = () => SugarElement.fromTag("col");
  const createRow = (columns, rowHeaders, columnHeaders, rowIndex) => {
    const tr = SugarElement.fromTag("tr");
    for (let j = 0; j < columns; j++) {
      const td = rowIndex < rowHeaders || j < columnHeaders ? tableHeaderCell() : tableCell();
      if (j < columnHeaders) {
        set$2(td, "scope", "row");
      }
      if (rowIndex < rowHeaders) {
        set$2(td, "scope", "col");
      }
      append$1(td, SugarElement.fromTag("br"));
      append$1(tr, td);
    }
    return tr;
  };
  const createGroupRow = columns => {
    const columnGroup = SugarElement.fromTag("colgroup");
    range$1(columns, () => append$1(columnGroup, tableColumn()));
    return columnGroup;
  };
  const createRows = (rows, columns, rowHeaders, columnHeaders) => range$1(rows, r => createRow(columns, rowHeaders, columnHeaders, r));
  const render = (rows, columns, rowHeaders, columnHeaders, headerType, renderOpts = DefaultRenderOptions) => {
    const table = SugarElement.fromTag("table");
    const rowHeadersGoInThead = headerType !== "cells";
    setAll(table, renderOpts.styles);
    setAll$1(table, renderOpts.attributes);
    if (renderOpts.colGroups) {
      append$1(table, createGroupRow(columns));
    }
    const actualRowHeaders = Math.min(rows, rowHeaders);
    if (rowHeadersGoInThead && rowHeaders > 0) {
      const thead = SugarElement.fromTag("thead");
      append$1(table, thead);
      const theadRowHeaders = headerType === "sectionCells" ? actualRowHeaders : 0;
      const theadRows = createRows(rowHeaders, columns, theadRowHeaders, columnHeaders);
      append(thead, theadRows);
    }
    const tbody = SugarElement.fromTag("tbody");
    append$1(table, tbody);
    const numRows = rowHeadersGoInThead ? rows - actualRowHeaders : rows;
    const numRowHeaders = rowHeadersGoInThead ? 0 : rowHeaders;
    const tbodyRows = createRows(numRows, columns, numRowHeaders, columnHeaders);
    append(tbody, tbodyRows);
    return table;
  };

  const get$4 = element => element.dom.innerHTML;
  const getOuter = element => {
    const container = SugarElement.fromTag("div");
    const clone = SugarElement.fromDom(element.dom.cloneNode(true));
    append$1(container, clone);
    return get$4(container);
  };

  const placeCaretInCell = (editor, cell) => {
    editor.selection.select(cell.dom, true);
    editor.selection.collapse(true);
  };
  const selectFirstCellInTable = (editor, tableElm) => {
    descendant(tableElm, "td,th").each(curry(placeCaretInCell, editor));
  };
  const fireEvents = (editor, table) => {
    each$2(descendants(table, "tr"), row => {
      fireNewRow(editor, row.dom);
      each$2(descendants(row, "th,td"), cell => {
        fireNewCell(editor, cell.dom);
      });
    });
  };
  const isPercentage = width => isString(width) && width.indexOf("%") !== -1;
  const insert = (editor, columns, rows, colHeaders, rowHeaders) => {
    const defaultStyles = getTableDefaultStyles(editor);
    const options = {
      styles: defaultStyles,
      attributes: getTableDefaultAttributes(editor),
      colGroups: tableUseColumnGroup(editor)
    };
    editor.undoManager.ignore(() => {
      const table = render(rows, columns, rowHeaders, colHeaders, getTableHeaderType(editor), options);
      set$2(table, "data-mce-id", "__mce");
      const html = getOuter(table);
      editor.insertContent(html);
      editor.addVisual();
    });
    return descendant(getBody(editor), "table[data-mce-id=\"__mce\"]").map(table => {
      if (isTablePixelsForced(editor)) {
        convertToPixelSizeWidth(table);
      } else if (isTableResponsiveForced(editor)) {
        convertToNoneSizeWidth(table);
      } else if (isTablePercentagesForced(editor) || isPercentage(defaultStyles.width)) {
        convertToPercentSizeWidth(table);
      }
      removeDataStyle(table);
      remove$7(table, "data-mce-id");
      fireEvents(editor, table);
      selectFirstCellInTable(editor, table);
      return table.dom;
    }).getOrNull();
  };
  const insertTable = (editor, rows, columns, options = {}) => {
    const checkInput = val => isNumber(val) && val > 0;
    if (checkInput(rows) && checkInput(columns)) {
      const headerRows = options.headerRows || 0;
      const headerColumns = options.headerColumns || 0;
      return insert(editor, columns, rows, headerColumns, headerRows);
    } else {
      console.error("Invalid values for mceInsertTable - rows and columns values are required to insert a table.");
      return null;
    }
  };

  var global = tinymce.util.Tools.resolve("tinymce.FakeClipboard");

  const tableTypeBase = "x-tinymce/dom-table-";
  const tableTypeRow = tableTypeBase + "rows";
  const tableTypeColumn = tableTypeBase + "columns";
  const setData = items => {
    const fakeClipboardItem = global.FakeClipboardItem(items);
    global.write([fakeClipboardItem]);
  };
  const getData = type => {
    var _a;
    const items = (_a = global.read()) !== null && _a !== void 0 ? _a : [];
    return findMap(items, item => Optional.from(item.getType(type)));
  };
  const clearData = type => {
    if (getData(type).isSome()) {
      global.clear();
    }
  };
  const setRows = rowsOpt => {
    rowsOpt.fold(clearRows, rows => setData({ [tableTypeRow]: rows }));
  };
  const getRows = () => getData(tableTypeRow);
  const clearRows = () => clearData(tableTypeRow);
  const setColumns = columnsOpt => {
    columnsOpt.fold(clearColumns, columns => setData({ [tableTypeColumn]: columns }));
  };
  const getColumns = () => getData(tableTypeColumn);
  const clearColumns = () => clearData(tableTypeColumn);

  const getSelectionStartCellOrCaption = editor => getSelectionCellOrCaption(getSelectionStart(editor), getIsRoot(editor)).filter(isInEditableContext$1);
  const getSelectionStartCell = editor => getSelectionCell(getSelectionStart(editor), getIsRoot(editor)).filter(isInEditableContext$1);
  const registerCommands = (editor, actions) => {
    const isRoot = getIsRoot(editor);
    const eraseTable = () => getSelectionStartCellOrCaption(editor).each(cellOrCaption => {
      table(cellOrCaption, isRoot).filter(not(isRoot)).each(table => {
        const cursor = SugarElement.fromText("");
        after$5(table, cursor);
        remove$6(table);
        if (editor.dom.isEmpty(editor.getBody())) {
          editor.setContent("");
          editor.selection.setCursorLocation();
        } else {
          const rng = editor.dom.createRng();
          rng.setStart(cursor.dom, 0);
          rng.setEnd(cursor.dom, 0);
          editor.selection.setRng(rng);
          editor.nodeChanged();
        }
      });
    });
    const setSizingMode = sizing => getSelectionStartCellOrCaption(editor).each(cellOrCaption => {
      const isForcedSizing = isTableResponsiveForced(editor) || isTablePixelsForced(editor) || isTablePercentagesForced(editor);
      if (!isForcedSizing) {
        table(cellOrCaption, isRoot).each(table => {
          if (sizing === "relative" && !isPercentSizing(table)) {
            convertToPercentSizeWidth(table);
          } else if (sizing === "fixed" && !isPixelSizing(table)) {
            convertToPixelSizeWidth(table);
          } else if (sizing === "responsive" && !isNoneSizing(table)) {
            convertToNoneSizeWidth(table);
          }
          removeDataStyle(table);
          fireTableModified(editor, table.dom, structureModified);
        });
      }
    });
    const getTableFromCell = cell => table(cell, isRoot);
    const performActionOnSelection = action => getSelectionStartCell(editor).bind(cell => getTableFromCell(cell).map(table => action(table, cell)));
    const toggleTableClass = (_ui, clazz) => {
      performActionOnSelection(table => {
        editor.formatter.toggle("tableclass", { value: clazz }, table.dom);
        fireTableModified(editor, table.dom, styleModified);
      });
    };
    const toggleTableCellClass = (_ui, clazz) => {
      performActionOnSelection(table => {
        const selectedCells = getCellsFromSelection(editor);
        const allHaveClass = forall(selectedCells, cell => editor.formatter.match("tablecellclass", { value: clazz }, cell.dom));
        const formatterAction = allHaveClass ? editor.formatter.remove : editor.formatter.apply;
        each$2(selectedCells, cell => formatterAction("tablecellclass", { value: clazz }, cell.dom));
        fireTableModified(editor, table.dom, styleModified);
      });
    };
    const toggleCaption = () => {
      getSelectionStartCellOrCaption(editor).each(cellOrCaption => {
        table(cellOrCaption, isRoot).each(table => {
          child(table, "caption").fold(() => {
            const caption = SugarElement.fromTag("caption");
            append$1(caption, SugarElement.fromText("Caption"));
            appendAt(table, caption, 0);
            editor.selection.setCursorLocation(caption.dom, 0);
          }, caption => {
            if (isTag("caption")(cellOrCaption)) {
              one("td", table).each(td => editor.selection.setCursorLocation(td.dom, 0));
            }
            remove$6(caption);
          });
          fireTableModified(editor, table.dom, structureModified);
        });
      });
    };
    const postExecute = _data => {
      editor.focus();
    };
    const actOnSelection = (execute, noEvents = false) => performActionOnSelection((table, startCell) => {
      const targets = forMenu(getCellsFromSelection(editor), table, startCell);
      execute(table, targets, noEvents).each(postExecute);
    });
    const copyRowSelection = () => performActionOnSelection((table, startCell) => {
      const targets = forMenu(getCellsFromSelection(editor), table, startCell);
      const generators = cellOperations(noop, SugarElement.fromDom(editor.getDoc()), Optional.none());
      return copyRows(table, targets, generators);
    });
    const copyColSelection = () => performActionOnSelection((table, startCell) => {
      const targets = forMenu(getCellsFromSelection(editor), table, startCell);
      return copyCols(table, targets);
    });
    const pasteOnSelection = (execute, getRows) => getRows().each(rows => {
      const clonedRows = map$1(rows, row => deep(row));
      performActionOnSelection((table, startCell) => {
        const generators = paste$1(SugarElement.fromDom(editor.getDoc()));
        const targets = pasteRows(getCellsFromSelection(editor), startCell, clonedRows, generators);
        execute(table, targets).each(postExecute);
      });
    });
    const actOnType = getAction => (_ui, args) => get$c(args, "type").each(type => {
      actOnSelection(getAction(type), args.no_events);
    });
    each$1({
      mceTableSplitCells: () => actOnSelection(actions.unmergeCells),
      mceTableMergeCells: () => actOnSelection(actions.mergeCells),
      mceTableInsertRowBefore: () => actOnSelection(actions.insertRowsBefore),
      mceTableInsertRowAfter: () => actOnSelection(actions.insertRowsAfter),
      mceTableInsertColBefore: () => actOnSelection(actions.insertColumnsBefore),
      mceTableInsertColAfter: () => actOnSelection(actions.insertColumnsAfter),
      mceTableDeleteCol: () => actOnSelection(actions.deleteColumn),
      mceTableDeleteRow: () => actOnSelection(actions.deleteRow),
      mceTableCutCol: () => copyColSelection().each(selection => {
        setColumns(selection);
        actOnSelection(actions.deleteColumn);
      }),
      mceTableCutRow: () => copyRowSelection().each(selection => {
        setRows(selection);
        actOnSelection(actions.deleteRow);
      }),
      mceTableCopyCol: () => copyColSelection().each(selection => setColumns(selection)),
      mceTableCopyRow: () => copyRowSelection().each(selection => setRows(selection)),
      mceTablePasteColBefore: () => pasteOnSelection(actions.pasteColsBefore, getColumns),
      mceTablePasteColAfter: () => pasteOnSelection(actions.pasteColsAfter, getColumns),
      mceTablePasteRowBefore: () => pasteOnSelection(actions.pasteRowsBefore, getRows),
      mceTablePasteRowAfter: () => pasteOnSelection(actions.pasteRowsAfter, getRows),
      mceTableDelete: eraseTable,
      mceTableCellToggleClass: toggleTableCellClass,
      mceTableToggleClass: toggleTableClass,
      mceTableToggleCaption: toggleCaption,
      mceTableSizingMode: (_ui, sizing) => setSizingMode(sizing),
      mceTableCellType: actOnType(type => type === "th" ? actions.makeCellsHeader : actions.unmakeCellsHeader),
      mceTableColType: actOnType(type => type === "th" ? actions.makeColumnsHeader : actions.unmakeColumnsHeader),
      mceTableRowType: actOnType(type => {
        switch (type) {
          case "header":
            return actions.makeRowsHeader;
          case "footer":
            return actions.makeRowsFooter;
          default:
            return actions.makeRowsBody;
        }
      })
    }, (func, name) => editor.addCommand(name, func));
    editor.addCommand("mceInsertTable", (_ui, args) => {
      insertTable(editor, args.rows, args.columns, args.options);
    });
    editor.addCommand("mceTableApplyCellStyle", (_ui, args) => {
      const getFormatName = style => "tablecell" + style.toLowerCase().replace("-", "");
      if (!isObject(args)) {
        return;
      }
      const cells = filter$2(getCellsFromSelection(editor), isInEditableContext$1);
      if (cells.length === 0) {
        return;
      }
      const validArgs = filter$1(args, (value, style) => editor.formatter.has(getFormatName(style)) && isString(value));
      if (isEmpty(validArgs)) {
        return;
      }
      each$1(validArgs, (value, style) => {
        const formatName = getFormatName(style);
        each$2(cells, cell => {
          if (value === "") {
            editor.formatter.remove(formatName, { value: null }, cell.dom, true);
          } else {
            editor.formatter.apply(formatName, { value }, cell.dom);
          }
        });
      });
      getTableFromCell(cells[0]).each(table => fireTableModified(editor, table.dom, styleModified));
    });
  };

  const registerQueryCommands = (editor, actions) => {
    const isRoot = getIsRoot(editor);
    const lookupOnSelection = action => getSelectionCell(getSelectionStart(editor)).bind(cell => table(cell, isRoot).map(table => {
      const targets = forMenu(getCellsFromSelection(editor), table, cell);
      return action(table, targets);
    })).getOr("");
    each$1({
      mceTableRowType: () => lookupOnSelection(actions.getTableRowType),
      mceTableCellType: () => lookupOnSelection(actions.getTableCellType),
      mceTableColType: () => lookupOnSelection(actions.getTableColType)
    }, (func, name) => editor.addQueryValueHandler(name, func));
  };

  const adt$4 = Adt.generate([
    { before: ["element"] },
    {
      on: [
        "element",
        "offset"
      ]
    },
    { after: ["element"] }
  ]);
  const cata$1 = (subject, onBefore, onOn, onAfter) => subject.fold(onBefore, onOn, onAfter);
  const getStart$1 = situ => situ.fold(identity, identity, identity);
  const before$2 = adt$4.before;
  const on = adt$4.on;
  const after$3 = adt$4.after;
  const Situ = {
    before: before$2,
    on,
    after: after$3,
    cata: cata$1,
    getStart: getStart$1
  };

  const create$4 = (selection, kill) => ({
    selection,
    kill
  });
  const Response = { create: create$4 };

  const selectNode = (win, element) => {
    const rng = win.document.createRange();
    rng.selectNode(element.dom);
    return rng;
  };
  const selectNodeContents = (win, element) => {
    const rng = win.document.createRange();
    selectNodeContentsUsing(rng, element);
    return rng;
  };
  const selectNodeContentsUsing = (rng, element) => rng.selectNodeContents(element.dom);
  const setStart = (rng, situ) => {
    situ.fold(e => {
      rng.setStartBefore(e.dom);
    }, (e, o) => {
      rng.setStart(e.dom, o);
    }, e => {
      rng.setStartAfter(e.dom);
    });
  };
  const setFinish = (rng, situ) => {
    situ.fold(e => {
      rng.setEndBefore(e.dom);
    }, (e, o) => {
      rng.setEnd(e.dom, o);
    }, e => {
      rng.setEndAfter(e.dom);
    });
  };
  const relativeToNative = (win, startSitu, finishSitu) => {
    const range = win.document.createRange();
    setStart(range, startSitu);
    setFinish(range, finishSitu);
    return range;
  };
  const exactToNative = (win, start, soffset, finish, foffset) => {
    const rng = win.document.createRange();
    rng.setStart(start.dom, soffset);
    rng.setEnd(finish.dom, foffset);
    return rng;
  };
  const toRect = rect => ({
    left: rect.left,
    top: rect.top,
    right: rect.right,
    bottom: rect.bottom,
    width: rect.width,
    height: rect.height
  });
  const getFirstRect$1 = rng => {
    const rects = rng.getClientRects();
    const rect = rects.length > 0 ? rects[0] : rng.getBoundingClientRect();
    return rect.width > 0 || rect.height > 0 ? Optional.some(rect).map(toRect) : Optional.none();
  };

  const adt$3 = Adt.generate([
    {
      ltr: [
        "start",
        "soffset",
        "finish",
        "foffset"
      ]
    },
    {
      rtl: [
        "start",
        "soffset",
        "finish",
        "foffset"
      ]
    }
  ]);
  const fromRange = (win, type, range) => type(SugarElement.fromDom(range.startContainer), range.startOffset, SugarElement.fromDom(range.endContainer), range.endOffset);
  const getRanges = (win, selection) => selection.match({
    domRange: rng => {
      return {
        ltr: constant(rng),
        rtl: Optional.none
      };
    },
    relative: (startSitu, finishSitu) => {
      return {
        ltr: cached(() => relativeToNative(win, startSitu, finishSitu)),
        rtl: cached(() => Optional.some(relativeToNative(win, finishSitu, startSitu)))
      };
    },
    exact: (start, soffset, finish, foffset) => {
      return {
        ltr: cached(() => exactToNative(win, start, soffset, finish, foffset)),
        rtl: cached(() => Optional.some(exactToNative(win, finish, foffset, start, soffset)))
      };
    }
  });
  const doDiagnose = (win, ranges) => {
    const rng = ranges.ltr();
    if (rng.collapsed) {
      const reversed = ranges.rtl().filter(rev => rev.collapsed === false);
      return reversed.map(rev => adt$3.rtl(SugarElement.fromDom(rev.endContainer), rev.endOffset, SugarElement.fromDom(rev.startContainer), rev.startOffset)).getOrThunk(() => fromRange(win, adt$3.ltr, rng));
    } else {
      return fromRange(win, adt$3.ltr, rng);
    }
  };
  const diagnose = (win, selection) => {
    const ranges = getRanges(win, selection);
    return doDiagnose(win, ranges);
  };
  const asLtrRange = (win, selection) => {
    const diagnosis = diagnose(win, selection);
    return diagnosis.match({
      ltr: (start, soffset, finish, foffset) => {
        const rng = win.document.createRange();
        rng.setStart(start.dom, soffset);
        rng.setEnd(finish.dom, foffset);
        return rng;
      },
      rtl: (start, soffset, finish, foffset) => {
        const rng = win.document.createRange();
        rng.setStart(finish.dom, foffset);
        rng.setEnd(start.dom, soffset);
        return rng;
      }
    });
  };
  adt$3.ltr;
  adt$3.rtl;

  const create$3 = (start, soffset, finish, foffset) => ({
    start,
    soffset,
    finish,
    foffset
  });
  const SimRange = { create: create$3 };

  const create$2 = (start, soffset, finish, foffset) => {
    return {
      start: Situ.on(start, soffset),
      finish: Situ.on(finish, foffset)
    };
  };
  const Situs = { create: create$2 };

  const convertToRange = (win, selection) => {
    const rng = asLtrRange(win, selection);
    return SimRange.create(SugarElement.fromDom(rng.startContainer), rng.startOffset, SugarElement.fromDom(rng.endContainer), rng.endOffset);
  };
  const makeSitus = Situs.create;

  const sync = (container, isRoot, start, soffset, finish, foffset, selectRange) => {
    if (!(eq$1(start, finish) && soffset === foffset)) {
      return closest$1(start, "td,th", isRoot).bind(s => {
        return closest$1(finish, "td,th", isRoot).bind(f => {
          return detect(container, isRoot, s, f, selectRange);
        });
      });
    } else {
      return Optional.none();
    }
  };
  const detect = (container, isRoot, start, finish, selectRange) => {
    if (!eq$1(start, finish)) {
      return identify(start, finish, isRoot).bind(cellSel => {
        const boxes = cellSel.boxes.getOr([]);
        if (boxes.length > 1) {
          selectRange(container, boxes, cellSel.start, cellSel.finish);
          return Optional.some(Response.create(Optional.some(makeSitus(start, 0, start, getEnd(start))), true));
        } else {
          return Optional.none();
        }
      });
    } else {
      return Optional.none();
    }
  };
  const update = (rows, columns, container, selected, annotations) => {
    const updateSelection = newSels => {
      annotations.clearBeforeUpdate(container);
      annotations.selectRange(container, newSels.boxes, newSels.start, newSels.finish);
      return newSels.boxes;
    };
    return shiftSelection(selected, rows, columns, annotations.firstSelectedSelector, annotations.lastSelectedSelector).map(updateSelection);
  };

  const traverse = (item, mode) => ({
    item,
    mode
  });
  const backtrack = (universe, item, _direction, transition = sidestep) => {
    return universe.property().parent(item).map(p => {
      return traverse(p, transition);
    });
  };
  const sidestep = (universe, item, direction, transition = advance) => {
    return direction.sibling(universe, item).map(p => {
      return traverse(p, transition);
    });
  };
  const advance = (universe, item, direction, transition = advance) => {
    const children = universe.property().children(item);
    const result = direction.first(children);
    return result.map(r => {
      return traverse(r, transition);
    });
  };
  const successors = [
    {
      current: backtrack,
      next: sidestep,
      fallback: Optional.none()
    },
    {
      current: sidestep,
      next: advance,
      fallback: Optional.some(backtrack)
    },
    {
      current: advance,
      next: advance,
      fallback: Optional.some(sidestep)
    }
  ];
  const go = (universe, item, mode, direction, rules = successors) => {
    const ruleOpt = find$1(rules, succ => {
      return succ.current === mode;
    });
    return ruleOpt.bind(rule => {
      return rule.current(universe, item, direction, rule.next).orThunk(() => {
        return rule.fallback.bind(fb => {
          return go(universe, item, fb, direction);
        });
      });
    });
  };

  const left$1 = () => {
    const sibling = (universe, item) => {
      return universe.query().prevSibling(item);
    };
    const first = children => {
      return children.length > 0 ? Optional.some(children[children.length - 1]) : Optional.none();
    };
    return {
      sibling,
      first
    };
  };
  const right$1 = () => {
    const sibling = (universe, item) => {
      return universe.query().nextSibling(item);
    };
    const first = children => {
      return children.length > 0 ? Optional.some(children[0]) : Optional.none();
    };
    return {
      sibling,
      first
    };
  };
  const Walkers = {
    left: left$1,
    right: right$1
  };

  const hone = (universe, item, predicate, mode, direction, isRoot) => {
    const next = go(universe, item, mode, direction);
    return next.bind(n => {
      if (isRoot(n.item)) {
        return Optional.none();
      } else {
        return predicate(n.item) ? Optional.some(n.item) : hone(universe, n.item, predicate, n.mode, direction, isRoot);
      }
    });
  };
  const left = (universe, item, predicate, isRoot) => {
    return hone(universe, item, predicate, sidestep, Walkers.left(), isRoot);
  };
  const right = (universe, item, predicate, isRoot) => {
    return hone(universe, item, predicate, sidestep, Walkers.right(), isRoot);
  };

  const isLeaf = universe => element => universe.property().children(element).length === 0;
  const before$1 = (universe, item, isRoot) => {
    return seekLeft$1(universe, item, isLeaf(universe), isRoot);
  };
  const after$2 = (universe, item, isRoot) => {
    return seekRight$1(universe, item, isLeaf(universe), isRoot);
  };
  const seekLeft$1 = left;
  const seekRight$1 = right;

  const universe = DomUniverse();
  const before = (element, isRoot) => {
    return before$1(universe, element, isRoot);
  };
  const after$1 = (element, isRoot) => {
    return after$2(universe, element, isRoot);
  };
  const seekLeft = (element, predicate, isRoot) => {
    return seekLeft$1(universe, element, predicate, isRoot);
  };
  const seekRight = (element, predicate, isRoot) => {
    return seekRight$1(universe, element, predicate, isRoot);
  };

  const ancestor = (scope, predicate, isRoot) => ancestor$2(scope, predicate, isRoot).isSome();

  const adt$2 = Adt.generate([
    { none: ["message"] },
    { success: [] },
    { failedUp: ["cell"] },
    { failedDown: ["cell"] }
  ]);
  const isOverlapping = (bridge, before, after) => {
    const beforeBounds = bridge.getRect(before);
    const afterBounds = bridge.getRect(after);
    return afterBounds.right > beforeBounds.left && afterBounds.left < beforeBounds.right;
  };
  const isRow = elem => {
    return closest$1(elem, "tr");
  };
  const verify = (bridge, before, beforeOffset, after, afterOffset, failure, isRoot) => {
    return closest$1(after, "td,th", isRoot).bind(afterCell => {
      return closest$1(before, "td,th", isRoot).map(beforeCell => {
        if (!eq$1(afterCell, beforeCell)) {
          return sharedOne(isRow, [
            afterCell,
            beforeCell
          ]).fold(() => {
            return isOverlapping(bridge, beforeCell, afterCell) ? adt$2.success() : failure(beforeCell);
          }, _sharedRow => {
            return failure(beforeCell);
          });
        } else {
          return eq$1(after, afterCell) && getEnd(afterCell) === afterOffset ? failure(beforeCell) : adt$2.none("in same cell");
        }
      });
    }).getOr(adt$2.none("default"));
  };
  const cata = (subject, onNone, onSuccess, onFailedUp, onFailedDown) => {
    return subject.fold(onNone, onSuccess, onFailedUp, onFailedDown);
  };
  const BeforeAfter = {
    ...adt$2,
    verify,
    cata
  };

  const inParent = (parent, children, element, index) => ({
    parent,
    children,
    element,
    index
  });
  const indexInParent = element => parent(element).bind(parent => {
    const children = children$2(parent);
    return indexOf(children, element).map(index => inParent(parent, children, element, index));
  });
  const indexOf = (elements, element) => findIndex(elements, curry(eq$1, element));

  const isBr = isTag("br");
  const gatherer = (cand, gather, isRoot) => {
    return gather(cand, isRoot).bind(target => {
      return isText(target) && get$6(target).trim().length === 0 ? gatherer(target, gather, isRoot) : Optional.some(target);
    });
  };
  const handleBr = (isRoot, element, direction) => {
    return direction.traverse(element).orThunk(() => {
      return gatherer(element, direction.gather, isRoot);
    }).map(direction.relative);
  };
  const findBr = (element, offset) => {
    return child$2(element, offset).filter(isBr).orThunk(() => {
      return child$2(element, offset - 1).filter(isBr);
    });
  };
  const handleParent = (isRoot, element, offset, direction) => {
    return findBr(element, offset).bind(br => {
      return direction.traverse(br).fold(() => {
        return gatherer(br, direction.gather, isRoot).map(direction.relative);
      }, adjacent => {
        return indexInParent(adjacent).map(info => {
          return Situ.on(info.parent, info.index);
        });
      });
    });
  };
  const tryBr = (isRoot, element, offset, direction) => {
    const target = isBr(element) ? handleBr(isRoot, element, direction) : handleParent(isRoot, element, offset, direction);
    return target.map(tgt => {
      return {
        start: tgt,
        finish: tgt
      };
    });
  };
  const process = analysis => {
    return BeforeAfter.cata(analysis, _message => {
      return Optional.none();
    }, () => {
      return Optional.none();
    }, cell => {
      return Optional.some(point(cell, 0));
    }, cell => {
      return Optional.some(point(cell, getEnd(cell)));
    });
  };

  const moveDown = (caret, amount) => {
    return {
      left: caret.left,
      top: caret.top + amount,
      right: caret.right,
      bottom: caret.bottom + amount
    };
  };
  const moveUp = (caret, amount) => {
    return {
      left: caret.left,
      top: caret.top - amount,
      right: caret.right,
      bottom: caret.bottom - amount
    };
  };
  const translate = (caret, xDelta, yDelta) => {
    return {
      left: caret.left + xDelta,
      top: caret.top + yDelta,
      right: caret.right + xDelta,
      bottom: caret.bottom + yDelta
    };
  };
  const getTop = caret => {
    return caret.top;
  };
  const getBottom = caret => {
    return caret.bottom;
  };

  const getPartialBox = (bridge, element, offset) => {
    if (offset >= 0 && offset < getEnd(element)) {
      return bridge.getRangedRect(element, offset, element, offset + 1);
    } else if (offset > 0) {
      return bridge.getRangedRect(element, offset - 1, element, offset);
    }
    return Optional.none();
  };
  const toCaret = rect => ({
    left: rect.left,
    top: rect.top,
    right: rect.right,
    bottom: rect.bottom
  });
  const getElemBox = (bridge, element) => {
    return Optional.some(bridge.getRect(element));
  };
  const getBoxAt = (bridge, element, offset) => {
    if (isElement(element)) {
      return getElemBox(bridge, element).map(toCaret);
    } else if (isText(element)) {
      return getPartialBox(bridge, element, offset).map(toCaret);
    } else {
      return Optional.none();
    }
  };
  const getEntireBox = (bridge, element) => {
    if (isElement(element)) {
      return getElemBox(bridge, element).map(toCaret);
    } else if (isText(element)) {
      return bridge.getRangedRect(element, 0, element, getEnd(element)).map(toCaret);
    } else {
      return Optional.none();
    }
  };

  const JUMP_SIZE = 5;
  const NUM_RETRIES = 100;
  const adt$1 = Adt.generate([
    { none: [] },
    { retry: ["caret"] }
  ]);
  const isOutside = (caret, box) => {
    return caret.left < box.left || Math.abs(box.right - caret.left) < 1 || caret.left > box.right;
  };
  const inOutsideBlock = (bridge, element, caret) => {
    return closest$2(element, isBlock).fold(never, cell => {
      return getEntireBox(bridge, cell).exists(box => {
        return isOutside(caret, box);
      });
    });
  };
  const adjustDown = (bridge, element, guessBox, original, caret) => {
    const lowerCaret = moveDown(caret, JUMP_SIZE);
    if (Math.abs(guessBox.bottom - original.bottom) < 1) {
      return adt$1.retry(lowerCaret);
    } else if (guessBox.top > caret.bottom) {
      return adt$1.retry(lowerCaret);
    } else if (guessBox.top === caret.bottom) {
      return adt$1.retry(moveDown(caret, 1));
    } else {
      return inOutsideBlock(bridge, element, caret) ? adt$1.retry(translate(lowerCaret, JUMP_SIZE, 0)) : adt$1.none();
    }
  };
  const adjustUp = (bridge, element, guessBox, original, caret) => {
    const higherCaret = moveUp(caret, JUMP_SIZE);
    if (Math.abs(guessBox.top - original.top) < 1) {
      return adt$1.retry(higherCaret);
    } else if (guessBox.bottom < caret.top) {
      return adt$1.retry(higherCaret);
    } else if (guessBox.bottom === caret.top) {
      return adt$1.retry(moveUp(caret, 1));
    } else {
      return inOutsideBlock(bridge, element, caret) ? adt$1.retry(translate(higherCaret, JUMP_SIZE, 0)) : adt$1.none();
    }
  };
  const upMovement = {
    point: getTop,
    adjuster: adjustUp,
    move: moveUp,
    gather: before
  };
  const downMovement = {
    point: getBottom,
    adjuster: adjustDown,
    move: moveDown,
    gather: after$1
  };
  const isAtTable = (bridge, x, y) => {
    return bridge.elementFromPoint(x, y).filter(elm => {
      return name(elm) === "table";
    }).isSome();
  };
  const adjustForTable = (bridge, movement, original, caret, numRetries) => {
    return adjustTil(bridge, movement, original, movement.move(caret, JUMP_SIZE), numRetries);
  };
  const adjustTil = (bridge, movement, original, caret, numRetries) => {
    if (numRetries === 0) {
      return Optional.some(caret);
    }
    if (isAtTable(bridge, caret.left, movement.point(caret))) {
      return adjustForTable(bridge, movement, original, caret, numRetries - 1);
    }
    return bridge.situsFromPoint(caret.left, movement.point(caret)).bind(guess => {
      return guess.start.fold(Optional.none, element => {
        return getEntireBox(bridge, element).bind(guessBox => {
          return movement.adjuster(bridge, element, guessBox, original, caret).fold(Optional.none, newCaret => {
            return adjustTil(bridge, movement, original, newCaret, numRetries - 1);
          });
        }).orThunk(() => {
          return Optional.some(caret);
        });
      }, Optional.none);
    });
  };
  const checkScroll = (movement, adjusted, bridge) => {
    if (movement.point(adjusted) > bridge.getInnerHeight()) {
      return Optional.some(movement.point(adjusted) - bridge.getInnerHeight());
    } else if (movement.point(adjusted) < 0) {
      return Optional.some(-movement.point(adjusted));
    } else {
      return Optional.none();
    }
  };
  const retry = (movement, bridge, caret) => {
    const moved = movement.move(caret, JUMP_SIZE);
    const adjusted = adjustTil(bridge, movement, caret, moved, NUM_RETRIES).getOr(moved);
    return checkScroll(movement, adjusted, bridge).fold(() => {
      return bridge.situsFromPoint(adjusted.left, movement.point(adjusted));
    }, delta => {
      bridge.scrollBy(0, delta);
      return bridge.situsFromPoint(adjusted.left, movement.point(adjusted) - delta);
    });
  };
  const Retries = {
    tryUp: curry(retry, upMovement),
    tryDown: curry(retry, downMovement),
    getJumpSize: constant(JUMP_SIZE)
  };

  const MAX_RETRIES = 20;
  const findSpot = (bridge, isRoot, direction) => {
    return bridge.getSelection().bind(sel => {
      return tryBr(isRoot, sel.finish, sel.foffset, direction).fold(() => {
        return Optional.some(point(sel.finish, sel.foffset));
      }, brNeighbour => {
        const range = bridge.fromSitus(brNeighbour);
        const analysis = BeforeAfter.verify(bridge, sel.finish, sel.foffset, range.finish, range.foffset, direction.failure, isRoot);
        return process(analysis);
      });
    });
  };
  const scan = (bridge, isRoot, element, offset, direction, numRetries) => {
    if (numRetries === 0) {
      return Optional.none();
    }
    return tryCursor(bridge, isRoot, element, offset, direction).bind(situs => {
      const range = bridge.fromSitus(situs);
      const analysis = BeforeAfter.verify(bridge, element, offset, range.finish, range.foffset, direction.failure, isRoot);
      return BeforeAfter.cata(analysis, () => {
        return Optional.none();
      }, () => {
        return Optional.some(situs);
      }, cell => {
        if (eq$1(element, cell) && offset === 0) {
          return tryAgain(bridge, element, offset, moveUp, direction);
        } else {
          return scan(bridge, isRoot, cell, 0, direction, numRetries - 1);
        }
      }, cell => {
        if (eq$1(element, cell) && offset === getEnd(cell)) {
          return tryAgain(bridge, element, offset, moveDown, direction);
        } else {
          return scan(bridge, isRoot, cell, getEnd(cell), direction, numRetries - 1);
        }
      });
    });
  };
  const tryAgain = (bridge, element, offset, move, direction) => {
    return getBoxAt(bridge, element, offset).bind(box => {
      return tryAt(bridge, direction, move(box, Retries.getJumpSize()));
    });
  };
  const tryAt = (bridge, direction, box) => {
    const browser = detect$2().browser;
    if (browser.isChromium() || browser.isSafari() || browser.isFirefox()) {
      return direction.retry(bridge, box);
    } else {
      return Optional.none();
    }
  };
  const tryCursor = (bridge, isRoot, element, offset, direction) => {
    return getBoxAt(bridge, element, offset).bind(box => {
      return tryAt(bridge, direction, box);
    });
  };
  const handle$1 = (bridge, isRoot, direction) => {
    return findSpot(bridge, isRoot, direction).bind(spot => {
      return scan(bridge, isRoot, spot.element, spot.offset, direction, MAX_RETRIES).map(bridge.fromSitus);
    });
  };

  const inSameTable = (elem, table) => {
    return ancestor(elem, e => {
      return parent(e).exists(p => {
        return eq$1(p, table);
      });
    });
  };
  const simulate = (bridge, isRoot, direction, initial, anchor) => {
    return closest$1(initial, "td,th", isRoot).bind(start => {
      return closest$1(start, "table", isRoot).bind(table => {
        if (!inSameTable(anchor, table)) {
          return Optional.none();
        }
        return handle$1(bridge, isRoot, direction).bind(range => {
          return closest$1(range.finish, "td,th", isRoot).map(finish => {
            return {
              start,
              finish,
              range
            };
          });
        });
      });
    });
  };
  const navigate = (bridge, isRoot, direction, initial, anchor, precheck) => {
    return precheck(initial, isRoot).orThunk(() => {
      return simulate(bridge, isRoot, direction, initial, anchor).map(info => {
        const range = info.range;
        return Response.create(Optional.some(makeSitus(range.start, range.soffset, range.finish, range.foffset)), true);
      });
    });
  };
  const firstUpCheck = (initial, isRoot) => {
    return closest$1(initial, "tr", isRoot).bind(startRow => {
      return closest$1(startRow, "table", isRoot).bind(table => {
        const rows = descendants(table, "tr");
        if (eq$1(startRow, rows[0])) {
          return seekLeft(table, element => {
            return last$1(element).isSome();
          }, isRoot).map(last => {
            const lastOffset = getEnd(last);
            return Response.create(Optional.some(makeSitus(last, lastOffset, last, lastOffset)), true);
          });
        } else {
          return Optional.none();
        }
      });
    });
  };
  const lastDownCheck = (initial, isRoot) => {
    return closest$1(initial, "tr", isRoot).bind(startRow => {
      return closest$1(startRow, "table", isRoot).bind(table => {
        const rows = descendants(table, "tr");
        if (eq$1(startRow, rows[rows.length - 1])) {
          return seekRight(table, element => {
            return first(element).isSome();
          }, isRoot).map(first => {
            return Response.create(Optional.some(makeSitus(first, 0, first, 0)), true);
          });
        } else {
          return Optional.none();
        }
      });
    });
  };
  const select = (bridge, container, isRoot, direction, initial, anchor, selectRange) => {
    return simulate(bridge, isRoot, direction, initial, anchor).bind(info => {
      return detect(container, isRoot, info.start, info.finish, selectRange);
    });
  };

  const Cell = initial => {
    let value = initial;
    const get = () => {
      return value;
    };
    const set = v => {
      value = v;
    };
    return {
      get,
      set
    };
  };

  const singleton = doRevoke => {
    const subject = Cell(Optional.none());
    const revoke = () => subject.get().each(doRevoke);
    const clear = () => {
      revoke();
      subject.set(Optional.none());
    };
    const isSet = () => subject.get().isSome();
    const get = () => subject.get();
    const set = s => {
      revoke();
      subject.set(Optional.some(s));
    };
    return {
      clear,
      isSet,
      get,
      set
    };
  };
  const value = () => {
    const subject = singleton(noop);
    const on = f => subject.get().each(f);
    return {
      ...subject,
      on
    };
  };

  const findCell = (target, isRoot) => closest$1(target, "td,th", isRoot);
  const isInEditableContext = cell => parentElement(cell).exists(isEditable$1);
  const MouseSelection = (bridge, container, isRoot, annotations) => {
    const cursor = value();
    const clearstate = cursor.clear;
    const applySelection = event => {
      cursor.on(start => {
        annotations.clearBeforeUpdate(container);
        findCell(event.target, isRoot).each(finish => {
          identify(start, finish, isRoot).each(cellSel => {
            const boxes = cellSel.boxes.getOr([]);
            if (boxes.length === 1) {
              const singleCell = boxes[0];
              const isNonEditableCell = getRaw(singleCell) === "false";
              const isCellClosestContentEditable = is(closest(event.target), singleCell, eq$1);
              if (isNonEditableCell && isCellClosestContentEditable) {
                annotations.selectRange(container, boxes, singleCell, singleCell);
              }
            } else if (boxes.length > 1) {
              annotations.selectRange(container, boxes, cellSel.start, cellSel.finish);
              bridge.selectContents(finish);
            }
          });
        });
      });
    };
    const mousedown = event => {
      annotations.clear(container);
      findCell(event.target, isRoot).filter(isInEditableContext).each(cursor.set);
    };
    const mouseover = event => {
      applySelection(event);
    };
    const mouseup = event => {
      applySelection(event);
      clearstate();
    };
    return {
      clearstate,
      mousedown,
      mouseover,
      mouseup
    };
  };

  const down = {
    traverse: nextSibling,
    gather: after$1,
    relative: Situ.before,
    retry: Retries.tryDown,
    failure: BeforeAfter.failedDown
  };
  const up = {
    traverse: prevSibling,
    gather: before,
    relative: Situ.before,
    retry: Retries.tryUp,
    failure: BeforeAfter.failedUp
  };

  const isKey = key => {
    return keycode => {
      return keycode === key;
    };
  };
  const isUp = isKey(38);
  const isDown = isKey(40);
  const isNavigation = keycode => {
    return keycode >= 37 && keycode <= 40;
  };
  const ltr = {
    isBackward: isKey(37),
    isForward: isKey(39)
  };
  const rtl = {
    isBackward: isKey(39),
    isForward: isKey(37)
  };

  const get$3 = _DOC => {
    const doc = _DOC !== undefined ? _DOC.dom : document;
    const x = doc.body.scrollLeft || doc.documentElement.scrollLeft;
    const y = doc.body.scrollTop || doc.documentElement.scrollTop;
    return SugarPosition(x, y);
  };
  const by = (x, y, _DOC) => {
    const doc = _DOC !== undefined ? _DOC.dom : document;
    const win = doc.defaultView;
    if (win) {
      win.scrollBy(x, y);
    }
  };

  const adt = Adt.generate([
    { domRange: ["rng"] },
    {
      relative: [
        "startSitu",
        "finishSitu"
      ]
    },
    {
      exact: [
        "start",
        "soffset",
        "finish",
        "foffset"
      ]
    }
  ]);
  const exactFromRange = simRange => adt.exact(simRange.start, simRange.soffset, simRange.finish, simRange.foffset);
  const getStart = selection => selection.match({
    domRange: rng => SugarElement.fromDom(rng.startContainer),
    relative: (startSitu, _finishSitu) => Situ.getStart(startSitu),
    exact: (start, _soffset, _finish, _foffset) => start
  });
  const domRange = adt.domRange;
  const relative = adt.relative;
  const exact = adt.exact;
  const getWin = selection => {
    const start = getStart(selection);
    return defaultView(start);
  };
  const range = SimRange.create;
  const SimSelection = {
    domRange,
    relative,
    exact,
    exactFromRange,
    getWin,
    range
  };

  const caretPositionFromPoint = (doc, x, y) => {
    var _a;
    return Optional.from((_a = doc.caretPositionFromPoint) === null || _a === void 0 ? void 0 : _a.call(doc, x, y)).bind(pos => {
      if (pos.offsetNode === null) {
        return Optional.none();
      }
      const r = doc.createRange();
      r.setStart(pos.offsetNode, pos.offset);
      r.collapse();
      return Optional.some(r);
    });
  };
  const caretRangeFromPoint = (doc, x, y) => {
    var _a;
    return Optional.from((_a = doc.caretRangeFromPoint) === null || _a === void 0 ? void 0 : _a.call(doc, x, y));
  };
  const availableSearch = (doc, x, y) => {
    if (doc.caretPositionFromPoint) {
      return caretPositionFromPoint(doc, x, y);
    } else if (doc.caretRangeFromPoint) {
      return caretRangeFromPoint(doc, x, y);
    } else {
      return Optional.none();
    }
  };
  const fromPoint = (win, x, y) => {
    const doc = win.document;
    return availableSearch(doc, x, y).map(rng => SimRange.create(SugarElement.fromDom(rng.startContainer), rng.startOffset, SugarElement.fromDom(rng.endContainer), rng.endOffset));
  };

  const beforeSpecial = (element, offset) => {
    const name$1 = name(element);
    if ("input" === name$1) {
      return Situ.after(element);
    } else if (!contains$2([
      "br",
      "img"
    ], name$1)) {
      return Situ.on(element, offset);
    } else {
      return offset === 0 ? Situ.before(element) : Situ.after(element);
    }
  };
  const preprocessRelative = (startSitu, finishSitu) => {
    const start = startSitu.fold(Situ.before, beforeSpecial, Situ.after);
    const finish = finishSitu.fold(Situ.before, beforeSpecial, Situ.after);
    return SimSelection.relative(start, finish);
  };
  const preprocessExact = (start, soffset, finish, foffset) => {
    const startSitu = beforeSpecial(start, soffset);
    const finishSitu = beforeSpecial(finish, foffset);
    return SimSelection.relative(startSitu, finishSitu);
  };

  const makeRange = (start, soffset, finish, foffset) => {
    const doc = owner(start);
    const rng = doc.dom.createRange();
    rng.setStart(start.dom, soffset);
    rng.setEnd(finish.dom, foffset);
    return rng;
  };
  const after = (start, soffset, finish, foffset) => {
    const r = makeRange(start, soffset, finish, foffset);
    const same = eq$1(start, finish) && soffset === foffset;
    return r.collapsed && !same;
  };

  const getNativeSelection = win => Optional.from(win.getSelection());
  const doSetNativeRange = (win, rng) => {
    getNativeSelection(win).each(selection => {
      selection.removeAllRanges();
      selection.addRange(rng);
    });
  };
  const doSetRange = (win, start, soffset, finish, foffset) => {
    const rng = exactToNative(win, start, soffset, finish, foffset);
    doSetNativeRange(win, rng);
  };
  const setLegacyRtlRange = (win, selection, start, soffset, finish, foffset) => {
    selection.collapse(start.dom, soffset);
    selection.extend(finish.dom, foffset);
  };
  const setRangeFromRelative = (win, relative) => diagnose(win, relative).match({
    ltr: (start, soffset, finish, foffset) => {
      doSetRange(win, start, soffset, finish, foffset);
    },
    rtl: (start, soffset, finish, foffset) => {
      getNativeSelection(win).each(selection => {
        if (selection.setBaseAndExtent) {
          selection.setBaseAndExtent(start.dom, soffset, finish.dom, foffset);
        } else if (selection.extend) {
          try {
            setLegacyRtlRange(win, selection, start, soffset, finish, foffset);
          } catch (_a) {
            doSetRange(win, finish, foffset, start, soffset);
          }
        } else {
          doSetRange(win, finish, foffset, start, soffset);
        }
      });
    }
  });
  const setExact = (win, start, soffset, finish, foffset) => {
    const relative = preprocessExact(start, soffset, finish, foffset);
    setRangeFromRelative(win, relative);
  };
  const setRelative = (win, startSitu, finishSitu) => {
    const relative = preprocessRelative(startSitu, finishSitu);
    setRangeFromRelative(win, relative);
  };
  const readRange = selection => {
    if (selection.rangeCount > 0) {
      const firstRng = selection.getRangeAt(0);
      const lastRng = selection.getRangeAt(selection.rangeCount - 1);
      return Optional.some(SimRange.create(SugarElement.fromDom(firstRng.startContainer), firstRng.startOffset, SugarElement.fromDom(lastRng.endContainer), lastRng.endOffset));
    } else {
      return Optional.none();
    }
  };
  const doGetExact = selection => {
    if (selection.anchorNode === null || selection.focusNode === null) {
      return readRange(selection);
    } else {
      const anchor = SugarElement.fromDom(selection.anchorNode);
      const focus = SugarElement.fromDom(selection.focusNode);
      return after(anchor, selection.anchorOffset, focus, selection.focusOffset) ? Optional.some(SimRange.create(anchor, selection.anchorOffset, focus, selection.focusOffset)) : readRange(selection);
    }
  };
  const setToElement = (win, element, selectNodeContents$1 = true) => {
    const rngGetter = selectNodeContents$1 ? selectNodeContents : selectNode;
    const rng = rngGetter(win, element);
    doSetNativeRange(win, rng);
  };
  const getExact = win => getNativeSelection(win).filter(sel => sel.rangeCount > 0).bind(doGetExact);
  const get$2 = win => getExact(win).map(range => SimSelection.exact(range.start, range.soffset, range.finish, range.foffset));
  const getFirstRect = (win, selection) => {
    const rng = asLtrRange(win, selection);
    return getFirstRect$1(rng);
  };
  const getAtPoint = (win, x, y) => fromPoint(win, x, y);
  const clear = win => {
    getNativeSelection(win).each(selection => selection.removeAllRanges());
  };

  const WindowBridge = win => {
    const elementFromPoint = (x, y) => {
      return SugarElement.fromPoint(SugarElement.fromDom(win.document), x, y);
    };
    const getRect = element => {
      return element.dom.getBoundingClientRect();
    };
    const getRangedRect = (start, soffset, finish, foffset) => {
      const sel = SimSelection.exact(start, soffset, finish, foffset);
      return getFirstRect(win, sel);
    };
    const getSelection = () => {
      return get$2(win).map(exactAdt => {
        return convertToRange(win, exactAdt);
      });
    };
    const fromSitus = situs => {
      const relative = SimSelection.relative(situs.start, situs.finish);
      return convertToRange(win, relative);
    };
    const situsFromPoint = (x, y) => {
      return getAtPoint(win, x, y).map(exact => {
        return Situs.create(exact.start, exact.soffset, exact.finish, exact.foffset);
      });
    };
    const clearSelection = () => {
      clear(win);
    };
    const collapseSelection = (toStart = false) => {
      get$2(win).each(sel => sel.fold(rng => rng.collapse(toStart), (startSitu, finishSitu) => {
        const situ = toStart ? startSitu : finishSitu;
        setRelative(win, situ, situ);
      }, (start, soffset, finish, foffset) => {
        const node = toStart ? start : finish;
        const offset = toStart ? soffset : foffset;
        setExact(win, node, offset, node, offset);
      }));
    };
    const selectNode = element => {
      setToElement(win, element, false);
    };
    const selectContents = element => {
      setToElement(win, element);
    };
    const setSelection = sel => {
      setExact(win, sel.start, sel.soffset, sel.finish, sel.foffset);
    };
    const setRelativeSelection = (start, finish) => {
      setRelative(win, start, finish);
    };
    const getInnerHeight = () => {
      return win.innerHeight;
    };
    const getScrollY = () => {
      const pos = get$3(SugarElement.fromDom(win.document));
      return pos.top;
    };
    const scrollBy = (x, y) => {
      by(x, y, SugarElement.fromDom(win.document));
    };
    return {
      elementFromPoint,
      getRect,
      getRangedRect,
      getSelection,
      fromSitus,
      situsFromPoint,
      clearSelection,
      collapseSelection,
      setSelection,
      setRelativeSelection,
      selectNode,
      selectContents,
      getInnerHeight,
      getScrollY,
      scrollBy
    };
  };

  const rc = (rows, cols) => ({
    rows,
    cols
  });
  const mouse = (win, container, isRoot, annotations) => {
    const bridge = WindowBridge(win);
    const handlers = MouseSelection(bridge, container, isRoot, annotations);
    return {
      clearstate: handlers.clearstate,
      mousedown: handlers.mousedown,
      mouseover: handlers.mouseover,
      mouseup: handlers.mouseup
    };
  };
  const isEditableNode = node => closest$2(node, isHTMLElement).exists(isEditable$1);
  const isEditableSelection = (start, finish) => isEditableNode(start) || isEditableNode(finish);
  const keyboard = (win, container, isRoot, annotations) => {
    const bridge = WindowBridge(win);
    const clearToNavigate = () => {
      annotations.clear(container);
      return Optional.none();
    };
    const keydown = (event, start, soffset, finish, foffset, direction) => {
      const realEvent = event.raw;
      const keycode = realEvent.which;
      const shiftKey = realEvent.shiftKey === true;
      const handler = retrieve$1(container, annotations.selectedSelector).fold(() => {
        if (isNavigation(keycode) && !shiftKey) {
          annotations.clearBeforeUpdate(container);
        }
        if (isNavigation(keycode) && shiftKey && !isEditableSelection(start, finish)) {
          return Optional.none;
        } else if (isDown(keycode) && shiftKey) {
          return curry(select, bridge, container, isRoot, down, finish, start, annotations.selectRange);
        } else if (isUp(keycode) && shiftKey) {
          return curry(select, bridge, container, isRoot, up, finish, start, annotations.selectRange);
        } else if (isDown(keycode)) {
          return curry(navigate, bridge, isRoot, down, finish, start, lastDownCheck);
        } else if (isUp(keycode)) {
          return curry(navigate, bridge, isRoot, up, finish, start, firstUpCheck);
        } else {
          return Optional.none;
        }
      }, selected => {
        const update$1 = attempts => {
          return () => {
            const navigation = findMap(attempts, delta => {
              return update(delta.rows, delta.cols, container, selected, annotations);
            });
            return navigation.fold(() => {
              return getEdges(container, annotations.firstSelectedSelector, annotations.lastSelectedSelector).map(edges => {
                const relative = isDown(keycode) || direction.isForward(keycode) ? Situ.after : Situ.before;
                bridge.setRelativeSelection(Situ.on(edges.first, 0), relative(edges.table));
                annotations.clear(container);
                return Response.create(Optional.none(), true);
              });
            }, _ => {
              return Optional.some(Response.create(Optional.none(), true));
            });
          };
        };
        if (isNavigation(keycode) && shiftKey && !isEditableSelection(start, finish)) {
          return Optional.none;
        } else if (isDown(keycode) && shiftKey) {
          return update$1([rc(+1, 0)]);
        } else if (isUp(keycode) && shiftKey) {
          return update$1([rc(-1, 0)]);
        } else if (direction.isBackward(keycode) && shiftKey) {
          return update$1([
            rc(0, -1),
            rc(-1, 0)
          ]);
        } else if (direction.isForward(keycode) && shiftKey) {
          return update$1([
            rc(0, +1),
            rc(+1, 0)
          ]);
        } else if (isNavigation(keycode) && !shiftKey) {
          return clearToNavigate;
        } else {
          return Optional.none;
        }
      });
      return handler();
    };
    const keyup = (event, start, soffset, finish, foffset) => {
      return retrieve$1(container, annotations.selectedSelector).fold(() => {
        const realEvent = event.raw;
        const keycode = realEvent.which;
        const shiftKey = realEvent.shiftKey === true;
        if (!shiftKey) {
          return Optional.none();
        }
        if (isNavigation(keycode) && isEditableSelection(start, finish)) {
          return sync(container, isRoot, start, soffset, finish, foffset, annotations.selectRange);
        } else {
          return Optional.none();
        }
      }, Optional.none);
    };
    return {
      keydown,
      keyup
    };
  };
  const external = (win, container, isRoot, annotations) => {
    const bridge = WindowBridge(win);
    return (start, finish) => {
      annotations.clearBeforeUpdate(container);
      identify(start, finish, isRoot).each(cellSel => {
        const boxes = cellSel.boxes.getOr([]);
        annotations.selectRange(container, boxes, cellSel.start, cellSel.finish);
        bridge.selectContents(finish);
        bridge.collapseSelection();
      });
    };
  };

  const read = (element, attr) => {
    const value = get$b(element, attr);
    return value === undefined || value === "" ? [] : value.split(" ");
  };
  const add$2 = (element, attr, id) => {
    const old = read(element, attr);
    const nu = old.concat([id]);
    set$2(element, attr, nu.join(" "));
    return true;
  };
  const remove$4 = (element, attr, id) => {
    const nu = filter$2(read(element, attr), v => v !== id);
    if (nu.length > 0) {
      set$2(element, attr, nu.join(" "));
    } else {
      remove$7(element, attr);
    }
    return false;
  };

  const supports = element => element.dom.classList !== undefined;
  const get$1 = element => read(element, "class");
  const add$1 = (element, clazz) => add$2(element, "class", clazz);
  const remove$3 = (element, clazz) => remove$4(element, "class", clazz);

  const add = (element, clazz) => {
    if (supports(element)) {
      element.dom.classList.add(clazz);
    } else {
      add$1(element, clazz);
    }
  };
  const cleanClass = element => {
    const classList = supports(element) ? element.dom.classList : get$1(element);
    if (classList.length === 0) {
      remove$7(element, "class");
    }
  };
  const remove$2 = (element, clazz) => {
    if (supports(element)) {
      const classList = element.dom.classList;
      classList.remove(clazz);
    } else {
      remove$3(element, clazz);
    }
    cleanClass(element);
  };
  const has = (element, clazz) => supports(element) && element.dom.classList.contains(clazz);

  const remove$1 = (element, classes) => {
    each$2(classes, x => {
      remove$2(element, x);
    });
  };

  const addClass = clazz => element => {
    add(element, clazz);
  };
  const removeClasses = classes => element => {
    remove$1(element, classes);
  };

  const byClass = ephemera => {
    const addSelectionClass = addClass(ephemera.selected);
    const removeSelectionClasses = removeClasses([
      ephemera.selected,
      ephemera.lastSelected,
      ephemera.firstSelected
    ]);
    const clear = container => {
      const sels = descendants(container, ephemera.selectedSelector);
      each$2(sels, removeSelectionClasses);
    };
    const selectRange = (container, cells, start, finish) => {
      clear(container);
      each$2(cells, addSelectionClass);
      add(start, ephemera.firstSelected);
      add(finish, ephemera.lastSelected);
    };
    return {
      clearBeforeUpdate: clear,
      clear,
      selectRange,
      selectedSelector: ephemera.selectedSelector,
      firstSelectedSelector: ephemera.firstSelectedSelector,
      lastSelectedSelector: ephemera.lastSelectedSelector
    };
  };
  const byAttr = (ephemera, onSelection, onClear) => {
    const removeSelectionAttributes = element => {
      remove$7(element, ephemera.selected);
      remove$7(element, ephemera.firstSelected);
      remove$7(element, ephemera.lastSelected);
    };
    const addSelectionAttribute = element => {
      set$2(element, ephemera.selected, "1");
    };
    const clear = container => {
      clearBeforeUpdate(container);
      onClear();
    };
    const clearBeforeUpdate = container => {
      const sels = descendants(container, `${ephemera.selectedSelector},${ephemera.firstSelectedSelector},${ephemera.lastSelectedSelector}`);
      each$2(sels, removeSelectionAttributes);
    };
    const selectRange = (container, cells, start, finish) => {
      clear(container);
      each$2(cells, addSelectionAttribute);
      set$2(start, ephemera.firstSelected, "1");
      set$2(finish, ephemera.lastSelected, "1");
      onSelection(cells, start, finish);
    };
    return {
      clearBeforeUpdate,
      clear,
      selectRange,
      selectedSelector: ephemera.selectedSelector,
      firstSelectedSelector: ephemera.firstSelectedSelector,
      lastSelectedSelector: ephemera.lastSelectedSelector
    };
  };
  const SelectionAnnotation = {
    byClass,
    byAttr
  };

  const fold = (subject, onNone, onMultiple, onSingle) => {
    switch (subject.tag) {
      case "none":
        return onNone();
      case "single":
        return onSingle(subject.element);
      case "multiple":
        return onMultiple(subject.elements);
    }
  };
  const none = () => ({ tag: "none" });
  const multiple = elements => ({
    tag: "multiple",
    elements
  });
  const single = element => ({
    tag: "single",
    element
  });

  const Selections = (lazyRoot, getStart, selectedSelector) => {
    const get = () => retrieve(lazyRoot(), selectedSelector).fold(() => getStart().fold(none, single), multiple);
    return { get };
  };

  const getUpOrLeftCells = (grid, selectedCells) => {
    const upGrid = grid.slice(0, selectedCells[selectedCells.length - 1].row + 1);
    const upDetails = toDetailList(upGrid);
    return bind$2(upDetails, detail => {
      const slicedCells = detail.cells.slice(0, selectedCells[selectedCells.length - 1].column + 1);
      return map$1(slicedCells, cell => cell.element);
    });
  };
  const getDownOrRightCells = (grid, selectedCells) => {
    const downGrid = grid.slice(selectedCells[0].row + selectedCells[0].rowspan - 1, grid.length);
    const downDetails = toDetailList(downGrid);
    return bind$2(downDetails, detail => {
      const slicedCells = detail.cells.slice(selectedCells[0].column + selectedCells[0].colspan - 1, detail.cells.length);
      return map$1(slicedCells, cell => cell.element);
    });
  };
  const getOtherCells = (table, target, generators) => {
    const warehouse = Warehouse.fromTable(table);
    const details = onCells(warehouse, target);
    return details.map(selectedCells => {
      const grid = toGrid(warehouse, generators, false);
      const { rows } = extractGridDetails(grid);
      const upOrLeftCells = getUpOrLeftCells(rows, selectedCells);
      const downOrRightCells = getDownOrRightCells(rows, selectedCells);
      return {
        upOrLeftCells,
        downOrRightCells
      };
    });
  };

  const mkEvent = (target, x, y, stop, prevent, kill, raw) => ({
    target,
    x,
    y,
    stop,
    prevent,
    kill,
    raw
  });
  const fromRawEvent$1 = rawEvent => {
    const target = SugarElement.fromDom(getOriginalEventTarget(rawEvent).getOr(rawEvent.target));
    const stop = () => rawEvent.stopPropagation();
    const prevent = () => rawEvent.preventDefault();
    const kill = compose(prevent, stop);
    return mkEvent(target, rawEvent.clientX, rawEvent.clientY, stop, prevent, kill, rawEvent);
  };
  const handle = (filter, handler) => rawEvent => {
    if (filter(rawEvent)) {
      handler(fromRawEvent$1(rawEvent));
    }
  };
  const binder = (element, event, filter, handler, useCapture) => {
    const wrapped = handle(filter, handler);
    element.dom.addEventListener(event, wrapped, useCapture);
    return { unbind: curry(unbind, element, event, wrapped, useCapture) };
  };
  const bind$1 = (element, event, filter, handler) => binder(element, event, filter, handler, false);
  const unbind = (element, event, handler, useCapture) => {
    element.dom.removeEventListener(event, handler, useCapture);
  };

  const filter = always;
  const bind = (element, event, handler) => bind$1(element, event, filter, handler);
  const fromRawEvent = fromRawEvent$1;

  const hasInternalTarget = e => !has(SugarElement.fromDom(e.target), "ephox-snooker-resizer-bar");
  const TableCellSelectionHandler = (editor, resizeHandler) => {
    const cellSelection = Selections(() => SugarElement.fromDom(editor.getBody()), () => getSelectionCell(getSelectionStart(editor), getIsRoot(editor)), ephemera.selectedSelector);
    const onSelection = (cells, start, finish) => {
      const tableOpt = table(start);
      tableOpt.each(table => {
        const cellsDom = map$1(cells, cell => cell.dom);
        const cloneFormats = getTableCloneElements(editor);
        const generators = cellOperations(noop, SugarElement.fromDom(editor.getDoc()), cloneFormats);
        const selectedCells = getCellsFromSelection(editor);
        const otherCellsDom = getOtherCells(table, { selection: selectedCells }, generators).map(otherCells => map(otherCells, cellArr => map$1(cellArr, cell => cell.dom))).getOrUndefined();
        fireTableSelectionChange(editor, cellsDom, start.dom, finish.dom, otherCellsDom);
      });
    };
    const onClear = () => fireTableSelectionClear(editor);
    const annotations = SelectionAnnotation.byAttr(ephemera, onSelection, onClear);
    editor.on("init", _e => {
      const win = editor.getWin();
      const body = getBody(editor);
      const isRoot = getIsRoot(editor);
      const syncSelection = () => {
        const sel = editor.selection;
        const start = SugarElement.fromDom(sel.getStart());
        const end = SugarElement.fromDom(sel.getEnd());
        const shared = sharedOne(table, [
          start,
          end
        ]);
        shared.fold(() => annotations.clear(body), noop);
      };
      const mouseHandlers = mouse(win, body, isRoot, annotations);
      const keyHandlers = keyboard(win, body, isRoot, annotations);
      const external$1 = external(win, body, isRoot, annotations);
      const hasShiftKey = event => event.raw.shiftKey === true;
      editor.on("TableSelectorChange", e => external$1(e.start, e.finish));
      const handleResponse = (event, response) => {
        if (!hasShiftKey(event)) {
          return;
        }
        if (response.kill) {
          event.kill();
        }
        response.selection.each(ns => {
          const relative = SimSelection.relative(ns.start, ns.finish);
          const rng = asLtrRange(win, relative);
          editor.selection.setRng(rng);
        });
      };
      const keyup = event => {
        const wrappedEvent = fromRawEvent(event);
        if (wrappedEvent.raw.shiftKey && isNavigation(wrappedEvent.raw.which)) {
          const rng = editor.selection.getRng();
          const start = SugarElement.fromDom(rng.startContainer);
          const end = SugarElement.fromDom(rng.endContainer);
          keyHandlers.keyup(wrappedEvent, start, rng.startOffset, end, rng.endOffset).each(response => {
            handleResponse(wrappedEvent, response);
          });
        }
      };
      const keydown = event => {
        const wrappedEvent = fromRawEvent(event);
        resizeHandler.hide();
        const rng = editor.selection.getRng();
        const start = SugarElement.fromDom(rng.startContainer);
        const end = SugarElement.fromDom(rng.endContainer);
        const direction = onDirection(ltr, rtl)(SugarElement.fromDom(editor.selection.getStart()));
        keyHandlers.keydown(wrappedEvent, start, rng.startOffset, end, rng.endOffset, direction).each(response => {
          handleResponse(wrappedEvent, response);
        });
        resizeHandler.show();
      };
      const isLeftMouse = raw => raw.button === 0;
      const isLeftButtonPressed = raw => {
        if (raw.buttons === undefined) {
          return true;
        }
        return (raw.buttons & 1) !== 0;
      };
      const dragStart = _e => {
        mouseHandlers.clearstate();
      };
      const mouseDown = e => {
        if (isLeftMouse(e) && hasInternalTarget(e)) {
          mouseHandlers.mousedown(fromRawEvent(e));
        }
      };
      const mouseOver = e => {
        if (isLeftButtonPressed(e) && hasInternalTarget(e)) {
          mouseHandlers.mouseover(fromRawEvent(e));
        }
      };
      const mouseUp = e => {
        if (isLeftMouse(e) && hasInternalTarget(e)) {
          mouseHandlers.mouseup(fromRawEvent(e));
        }
      };
      const getDoubleTap = () => {
        const lastTarget = Cell(SugarElement.fromDom(body));
        const lastTimeStamp = Cell(0);
        const touchEnd = t => {
          const target = SugarElement.fromDom(t.target);
          if (isTag("td")(target) || isTag("th")(target)) {
            const lT = lastTarget.get();
            const lTS = lastTimeStamp.get();
            if (eq$1(lT, target) && t.timeStamp - lTS < 300) {
              t.preventDefault();
              external$1(target, target);
            }
          }
          lastTarget.set(target);
          lastTimeStamp.set(t.timeStamp);
        };
        return { touchEnd };
      };
      const doubleTap = getDoubleTap();
      editor.on("dragstart", dragStart);
      editor.on("mousedown", mouseDown);
      editor.on("mouseover", mouseOver);
      editor.on("mouseup", mouseUp);
      editor.on("touchend", doubleTap.touchEnd);
      editor.on("keyup", keyup);
      editor.on("keydown", keydown);
      editor.on("NodeChange", syncSelection);
    });
    editor.on("PreInit", () => {
      editor.serializer.addTempAttr(ephemera.firstSelected);
      editor.serializer.addTempAttr(ephemera.lastSelected);
    });
    const clearSelectedCells = container => annotations.clear(SugarElement.fromDom(container));
    const getSelectedCells = () => fold(cellSelection.get(), constant([]), cells => {
      return map$1(cells, cell => cell.dom);
    }, cell => [cell.dom]);
    return {
      getSelectedCells,
      clearSelectedCells
    };
  };

  const Event = fields => {
    let handlers = [];
    const bind = handler => {
      if (handler === undefined) {
        throw new Error("Event bind error: undefined handler");
      }
      handlers.push(handler);
    };
    const unbind = handler => {
      handlers = filter$2(handlers, h => {
        return h !== handler;
      });
    };
    const trigger = (...args) => {
      const event = {};
      each$2(fields, (name, i) => {
        event[name] = args[i];
      });
      each$2(handlers, handler => {
        handler(event);
      });
    };
    return {
      bind,
      unbind,
      trigger
    };
  };

  const create$1 = typeDefs => {
    const registry = map(typeDefs, event => {
      return {
        bind: event.bind,
        unbind: event.unbind
      };
    });
    const trigger = map(typeDefs, event => {
      return event.trigger;
    });
    return {
      registry,
      trigger
    };
  };

  const last = (fn, rate) => {
    let timer = null;
    const cancel = () => {
      if (!isNull(timer)) {
        clearTimeout(timer);
        timer = null;
      }
    };
    const throttle = (...args) => {
      cancel();
      timer = setTimeout(() => {
        timer = null;
        fn.apply(null, args);
      }, rate);
    };
    return {
      cancel,
      throttle
    };
  };

  const sort = arr => {
    return arr.slice(0).sort();
  };
  const reqMessage = (required, keys) => {
    throw new Error("All required keys (" + sort(required).join(", ") + ") were not specified. Specified keys were: " + sort(keys).join(", ") + ".");
  };
  const unsuppMessage = unsupported => {
    throw new Error("Unsupported keys for object: " + sort(unsupported).join(", "));
  };
  const validateStrArr = (label, array) => {
    if (!isArray(array)) {
      throw new Error("The " + label + " fields must be an array. Was: " + array + ".");
    }
    each$2(array, a => {
      if (!isString(a)) {
        throw new Error("The value " + a + " in the " + label + " fields was not a string.");
      }
    });
  };
  const invalidTypeMessage = (incorrect, type) => {
    throw new Error("All values need to be of type: " + type + ". Keys (" + sort(incorrect).join(", ") + ") were not.");
  };
  const checkDupes = everything => {
    const sorted = sort(everything);
    const dupe = find$1(sorted, (s, i) => {
      return i < sorted.length - 1 && s === sorted[i + 1];
    });
    dupe.each(d => {
      throw new Error("The field: " + d + " occurs more than once in the combined fields: [" + sorted.join(", ") + "].");
    });
  };

  const base = (handleUnsupported, required) => {
    return baseWith(handleUnsupported, required, {
      validate: isFunction,
      label: "function"
    });
  };
  const baseWith = (handleUnsupported, required, pred) => {
    if (required.length === 0) {
      throw new Error("You must specify at least one required field.");
    }
    validateStrArr("required", required);
    checkDupes(required);
    return obj => {
      const keys$1 = keys(obj);
      const allReqd = forall(required, req => {
        return contains$2(keys$1, req);
      });
      if (!allReqd) {
        reqMessage(required, keys$1);
      }
      handleUnsupported(required, keys$1);
      const invalidKeys = filter$2(required, key => {
        return !pred.validate(obj[key], key);
      });
      if (invalidKeys.length > 0) {
        invalidTypeMessage(invalidKeys, pred.label);
      }
      return obj;
    };
  };
  const handleExact = (required, keys) => {
    const unsupported = filter$2(keys, key => {
      return !contains$2(required, key);
    });
    if (unsupported.length > 0) {
      unsuppMessage(unsupported);
    }
  };
  const exactly = required => base(handleExact, required);

  const DragMode = exactly([
    "compare",
    "extract",
    "mutate",
    "sink"
  ]);
  const DragSink = exactly([
    "element",
    "start",
    "stop",
    "destroy"
  ]);
  const DragApi = exactly([
    "forceDrop",
    "drop",
    "move",
    "delayDrop"
  ]);

  const InDrag = () => {
    let previous = Optional.none();
    const reset = () => {
      previous = Optional.none();
    };
    const update = (mode, nu) => {
      const result = previous.map(old => {
        return mode.compare(old, nu);
      });
      previous = Optional.some(nu);
      return result;
    };
    const onEvent = (event, mode) => {
      const dataOption = mode.extract(event);
      dataOption.each(data => {
        const offset = update(mode, data);
        offset.each(d => {
          events.trigger.move(d);
        });
      });
    };
    const events = create$1({ move: Event(["info"]) });
    return {
      onEvent,
      reset,
      events: events.registry
    };
  };

  const NoDrag = () => {
    const events = create$1({ move: Event(["info"]) });
    return {
      onEvent: noop,
      reset: noop,
      events: events.registry
    };
  };

  const Movement = () => {
    const noDragState = NoDrag();
    const inDragState = InDrag();
    let dragState = noDragState;
    const on = () => {
      dragState.reset();
      dragState = inDragState;
    };
    const off = () => {
      dragState.reset();
      dragState = noDragState;
    };
    const onEvent = (event, mode) => {
      dragState.onEvent(event, mode);
    };
    const isOn = () => {
      return dragState === inDragState;
    };
    return {
      on,
      off,
      isOn,
      onEvent,
      events: inDragState.events
    };
  };

  const setup = (mutation, mode, settings) => {
    let active = false;
    const events = create$1({
      start: Event([]),
      stop: Event([])
    });
    const movement = Movement();
    const drop = () => {
      sink.stop();
      if (movement.isOn()) {
        movement.off();
        events.trigger.stop();
      }
    };
    const throttledDrop = last(drop, 200);
    const go = parent => {
      sink.start(parent);
      movement.on();
      events.trigger.start();
    };
    const mousemove = event => {
      throttledDrop.cancel();
      movement.onEvent(event, mode);
    };
    movement.events.move.bind(event => {
      mode.mutate(mutation, event.info);
    });
    const on = () => {
      active = true;
    };
    const off = () => {
      active = false;
    };
    const isActive = () => active;
    const runIfActive = f => {
      return (...args) => {
        if (active) {
          f.apply(null, args);
        }
      };
    };
    const sink = mode.sink(DragApi({
      forceDrop: drop,
      drop: runIfActive(drop),
      move: runIfActive(mousemove),
      delayDrop: runIfActive(throttledDrop.throttle)
    }), settings);
    const destroy = () => {
      sink.destroy();
    };
    return {
      element: sink.element,
      go,
      on,
      off,
      isActive,
      destroy,
      events: events.registry
    };
  };

  const css = namespace => {
    const dashNamespace = namespace.replace(/\./g, "-");
    const resolve = str => {
      return dashNamespace + "-" + str;
    };
    return { resolve };
  };

  const styles$1 = css("ephox-dragster");
  const resolve$1 = styles$1.resolve;

  const Blocker = options => {
    const settings = {
      layerClass: resolve$1("blocker"),
      ...options
    };
    const div = SugarElement.fromTag("div");
    set$2(div, "role", "presentation");
    setAll(div, {
      position: "fixed",
      left: "0px",
      top: "0px",
      width: "100%",
      height: "100%"
    });
    add(div, resolve$1("blocker"));
    add(div, settings.layerClass);
    const element = constant(div);
    const destroy = () => {
      remove$6(div);
    };
    return {
      element,
      destroy
    };
  };

  const compare = (old, nu) => {
    return SugarPosition(nu.left - old.left, nu.top - old.top);
  };
  const extract = event => {
    return Optional.some(SugarPosition(event.x, event.y));
  };
  const mutate = (mutation, info) => {
    mutation.mutate(info.left, info.top);
  };
  const sink = (dragApi, settings) => {
    const blocker = Blocker(settings);
    const mdown = bind(blocker.element(), "mousedown", dragApi.forceDrop);
    const mup = bind(blocker.element(), "mouseup", dragApi.drop);
    const mmove = bind(blocker.element(), "mousemove", dragApi.move);
    const mout = bind(blocker.element(), "mouseout", dragApi.delayDrop);
    const destroy = () => {
      blocker.destroy();
      mup.unbind();
      mmove.unbind();
      mout.unbind();
      mdown.unbind();
    };
    const start = parent => {
      append$1(parent, blocker.element());
    };
    const stop = () => {
      remove$6(blocker.element());
    };
    return DragSink({
      element: blocker.element,
      start,
      stop,
      destroy
    });
  };
  var MouseDrag = DragMode({
    compare,
    extract,
    sink,
    mutate
  });

  const transform = (mutation, settings = {}) => {
    var _a;
    const mode = (_a = settings.mode) !== null && _a !== void 0 ? _a : MouseDrag;
    return setup(mutation, mode, settings);
  };

  const styles = css("ephox-snooker");
  const resolve = styles.resolve;

  const Mutation = () => {
    const events = create$1({
      drag: Event([
        "xDelta",
        "yDelta"
      ])
    });
    const mutate = (x, y) => {
      events.trigger.drag(x, y);
    };
    return {
      mutate,
      events: events.registry
    };
  };

  const BarMutation = () => {
    const events = create$1({
      drag: Event([
        "xDelta",
        "yDelta",
        "target"
      ])
    });
    let target = Optional.none();
    const delegate = Mutation();
    delegate.events.drag.bind(event => {
      target.each(t => {
        events.trigger.drag(event.xDelta, event.yDelta, t);
      });
    });
    const assign = t => {
      target = Optional.some(t);
    };
    const get = () => {
      return target;
    };
    return {
      assign,
      get,
      mutate: delegate.mutate,
      events: events.registry
    };
  };

  const col = (column, x, y, w, h) => {
    const bar = SugarElement.fromTag("div");
    setAll(bar, {
      position: "absolute",
      left: x - w / 2 + "px",
      top: y + "px",
      height: h + "px",
      width: w + "px"
    });
    setAll$1(bar, {
      "data-column": column,
      "role": "presentation"
    });
    return bar;
  };
  const row = (r, x, y, w, h) => {
    const bar = SugarElement.fromTag("div");
    setAll(bar, {
      position: "absolute",
      left: x + "px",
      top: y - h / 2 + "px",
      height: h + "px",
      width: w + "px"
    });
    setAll$1(bar, {
      "data-row": r,
      "role": "presentation"
    });
    return bar;
  };

  const resizeBar = resolve("resizer-bar");
  const resizeRowBar = resolve("resizer-rows");
  const resizeColBar = resolve("resizer-cols");
  const BAR_THICKNESS = 7;
  const resizableRows = (warehouse, isResizable) => bind$2(warehouse.all, (row, i) => isResizable(row.element) ? [i] : []);
  const resizableColumns = (warehouse, isResizable) => {
    const resizableCols = [];
    range$1(warehouse.grid.columns, index => {
      const colElmOpt = Warehouse.getColumnAt(warehouse, index).map(col => col.element);
      if (colElmOpt.forall(isResizable)) {
        resizableCols.push(index);
      }
    });
    return filter$2(resizableCols, colIndex => {
      const columnCells = Warehouse.filterItems(warehouse, cell => cell.column === colIndex);
      return forall(columnCells, cell => isResizable(cell.element));
    });
  };
  const destroy = wire => {
    const previous = descendants(wire.parent(), "." + resizeBar);
    each$2(previous, remove$6);
  };
  const drawBar = (wire, positions, create) => {
    const origin = wire.origin();
    each$2(positions, cpOption => {
      cpOption.each(cp => {
        const bar = create(origin, cp);
        add(bar, resizeBar);
        append$1(wire.parent(), bar);
      });
    });
  };
  const refreshCol = (wire, colPositions, position, tableHeight) => {
    drawBar(wire, colPositions, (origin, cp) => {
      const colBar = col(cp.col, cp.x - origin.left, position.top - origin.top, BAR_THICKNESS, tableHeight);
      add(colBar, resizeColBar);
      return colBar;
    });
  };
  const refreshRow = (wire, rowPositions, position, tableWidth) => {
    drawBar(wire, rowPositions, (origin, cp) => {
      const rowBar = row(cp.row, position.left - origin.left, cp.y - origin.top, tableWidth, BAR_THICKNESS);
      add(rowBar, resizeRowBar);
      return rowBar;
    });
  };
  const refreshGrid = (warhouse, wire, table, rows, cols) => {
    const position = absolute(table);
    const isResizable = wire.isResizable;
    const rowPositions = rows.length > 0 ? height.positions(rows, table) : [];
    const resizableRowBars = rowPositions.length > 0 ? resizableRows(warhouse, isResizable) : [];
    const resizableRowPositions = filter$2(rowPositions, (_pos, i) => exists(resizableRowBars, barIndex => i === barIndex));
    refreshRow(wire, resizableRowPositions, position, getOuter$2(table));
    const colPositions = cols.length > 0 ? width.positions(cols, table) : [];
    const resizableColBars = colPositions.length > 0 ? resizableColumns(warhouse, isResizable) : [];
    const resizableColPositions = filter$2(colPositions, (_pos, i) => exists(resizableColBars, barIndex => i === barIndex));
    refreshCol(wire, resizableColPositions, position, getOuter$1(table));
  };
  const refresh = (wire, table) => {
    destroy(wire);
    if (wire.isResizable(table)) {
      const warehouse = Warehouse.fromTable(table);
      const rows$1 = rows(warehouse);
      const cols = columns(warehouse);
      refreshGrid(warehouse, wire, table, rows$1, cols);
    }
  };
  const each = (wire, f) => {
    const bars = descendants(wire.parent(), "." + resizeBar);
    each$2(bars, f);
  };
  const hide = wire => {
    each(wire, bar => {
      set$1(bar, "display", "none");
    });
  };
  const show = wire => {
    each(wire, bar => {
      set$1(bar, "display", "block");
    });
  };
  const isRowBar = element => {
    return has(element, resizeRowBar);
  };
  const isColBar = element => {
    return has(element, resizeColBar);
  };

  const resizeBarDragging = resolve("resizer-bar-dragging");
  const BarManager = wire => {
    const mutation = BarMutation();
    const resizing = transform(mutation, {});
    let hoverTable = Optional.none();
    const getResizer = (element, type) => {
      return Optional.from(get$b(element, type));
    };
    mutation.events.drag.bind(event => {
      getResizer(event.target, "data-row").each(_dataRow => {
        const currentRow = getCssValue(event.target, "top");
        set$1(event.target, "top", currentRow + event.yDelta + "px");
      });
      getResizer(event.target, "data-column").each(_dataCol => {
        const currentCol = getCssValue(event.target, "left");
        set$1(event.target, "left", currentCol + event.xDelta + "px");
      });
    });
    const getDelta = (target, dir) => {
      const newX = getCssValue(target, dir);
      const oldX = getAttrValue(target, "data-initial-" + dir, 0);
      return newX - oldX;
    };
    resizing.events.stop.bind(() => {
      mutation.get().each(target => {
        hoverTable.each(table => {
          getResizer(target, "data-row").each(row => {
            const delta = getDelta(target, "top");
            remove$7(target, "data-initial-top");
            events.trigger.adjustHeight(table, delta, parseInt(row, 10));
          });
          getResizer(target, "data-column").each(column => {
            const delta = getDelta(target, "left");
            remove$7(target, "data-initial-left");
            events.trigger.adjustWidth(table, delta, parseInt(column, 10));
          });
          refresh(wire, table);
        });
      });
    });
    const handler = (target, dir) => {
      events.trigger.startAdjust();
      mutation.assign(target);
      set$2(target, "data-initial-" + dir, getCssValue(target, dir));
      add(target, resizeBarDragging);
      set$1(target, "opacity", "0.2");
      resizing.go(wire.dragContainer());
    };
    const mousedown = bind(wire.parent(), "mousedown", event => {
      if (isRowBar(event.target)) {
        handler(event.target, "top");
      }
      if (isColBar(event.target)) {
        handler(event.target, "left");
      }
    });
    const isRoot = e => {
      return eq$1(e, wire.view());
    };
    const findClosestEditableTable = target => closest$1(target, "table", isRoot).filter(isEditable$1);
    const mouseover = bind(wire.view(), "mouseover", event => {
      findClosestEditableTable(event.target).fold(() => {
        if (inBody(event.target)) {
          destroy(wire);
        }
      }, table => {
        if (resizing.isActive()) {
          hoverTable = Optional.some(table);
          refresh(wire, table);
        }
      });
    });
    const destroy$1 = () => {
      mousedown.unbind();
      mouseover.unbind();
      resizing.destroy();
      destroy(wire);
    };
    const refresh$1 = tbl => {
      refresh(wire, tbl);
    };
    const events = create$1({
      adjustHeight: Event([
        "table",
        "delta",
        "row"
      ]),
      adjustWidth: Event([
        "table",
        "delta",
        "column"
      ]),
      startAdjust: Event([])
    });
    return {
      destroy: destroy$1,
      refresh: refresh$1,
      on: resizing.on,
      off: resizing.off,
      hideBars: curry(hide, wire),
      showBars: curry(show, wire),
      events: events.registry
    };
  };

  const create = (wire, resizing, lazySizing) => {
    const hdirection = height;
    const vdirection = width;
    const manager = BarManager(wire);
    const events = create$1({
      beforeResize: Event([
        "table",
        "type"
      ]),
      afterResize: Event([
        "table",
        "type"
      ]),
      startDrag: Event([])
    });
    manager.events.adjustHeight.bind(event => {
      const table = event.table;
      events.trigger.beforeResize(table, "row");
      const delta = hdirection.delta(event.delta, table);
      adjustHeight(table, delta, event.row);
      events.trigger.afterResize(table, "row");
    });
    manager.events.startAdjust.bind(_event => {
      events.trigger.startDrag();
    });
    manager.events.adjustWidth.bind(event => {
      const table = event.table;
      events.trigger.beforeResize(table, "col");
      const delta = vdirection.delta(event.delta, table);
      const tableSize = lazySizing(table);
      adjustWidth(table, delta, event.column, resizing, tableSize);
      events.trigger.afterResize(table, "col");
    });
    return {
      on: manager.on,
      off: manager.off,
      refreshBars: manager.refresh,
      hideBars: manager.hideBars,
      showBars: manager.showBars,
      destroy: manager.destroy,
      events: events.registry
    };
  };
  const TableResize = { create };

  const random = () => window.crypto.getRandomValues(new Uint32Array(1))[0] / 4294967295;

  let unique = 0;
  const generate = prefix => {
    const date = new Date();
    const time = date.getTime();
    const random$1 = Math.floor(random() * 1000000000);
    unique++;
    return prefix + "_" + random$1 + unique + String(time);
  };

  const only = (element, isResizable) => {
    const parent = isDocument(element) ? documentElement(element) : element;
    return {
      parent: constant(parent),
      view: constant(element),
      dragContainer: constant(parent),
      origin: constant(SugarPosition(0, 0)),
      isResizable
    };
  };
  const detached = (editable, chrome, isResizable) => {
    const origin = () => absolute(chrome);
    return {
      parent: constant(chrome),
      view: constant(editable),
      dragContainer: constant(chrome),
      origin,
      isResizable
    };
  };
  const body = (editable, chrome, isResizable) => {
    return {
      parent: constant(chrome),
      view: constant(editable),
      dragContainer: constant(chrome),
      origin: constant(SugarPosition(0, 0)),
      isResizable
    };
  };
  const scrollable = (editable, chrome, dragContainer, isResizable) => {
    return {
      parent: constant(chrome),
      view: constant(editable),
      dragContainer: constant(dragContainer),
      origin: () => absolute(chrome),
      isResizable
    };
  };
  const ResizeWire = {
    only,
    detached,
    body,
    scrollable
  };

  const createContainer = position => {
    const id = generate("resizer-container");
    const container = SugarElement.fromTag("div");
    set$2(container, "id", id);
    setAll(container, {
      position,
      height: "0",
      width: "0",
      padding: "0",
      margin: "0",
      border: "0"
    });
    return container;
  };
  const getInlineResizeWire = (editor, isResizable) => {
    const isSplitUiMode$1 = isSplitUiMode(editor);
    const editorBody = SugarElement.fromDom(editor.getBody());
    const container = createContainer(isSplitUiMode$1 ? "relative" : "static");
    const body = body$1();
    if (isSplitUiMode$1) {
      after$5(editorBody, container);
      return ResizeWire.scrollable(editorBody, container, body, isResizable);
    }
    append$1(body, container);
    return ResizeWire.body(editorBody, container, isResizable);
  };
  const get = (editor, isResizable) => {
    if (editor.inline) {
      return getInlineResizeWire(editor, isResizable);
    }
    return ResizeWire.only(SugarElement.fromDom(editor.getDoc()), isResizable);
  };
  const remove = (editor, wire) => {
    if (editor.inline) {
      remove$6(wire.parent());
    }
  };

  const isTable = node => isNonNullable(node) && node.nodeName === "TABLE";
  const barResizerPrefix = "bar-";
  const isResizable = elm => get$b(elm, "data-mce-resize") !== "false";
  const syncTableCellPixels = table => {
    const warehouse = Warehouse.fromTable(table);
    if (!Warehouse.hasColumns(warehouse)) {
      each$2(cells$1(table), cell => {
        const computedWidth = get$a(cell, "width");
        set$1(cell, "width", computedWidth);
        remove$7(cell, "width");
      });
    }
  };
  const isCornerResize = origin => startsWith(origin, "corner-");
  const getCornerLocation = origin => removeLeading(origin, "corner-");
  const TableResizeHandler = editor => {
    const selectionRng = value();
    const tableResize = value();
    const resizeWire = value();
    let startW;
    let startRawW;
    let startH;
    let startRawH;
    const lazySizing = table => get$5(editor, table);
    const lazyResizingBehaviour = () => isPreserveTableColumnResizing(editor) ? preserveTable() : resizeTable();
    const getNumColumns = table => getGridSize(table).columns;
    const getNumRows = table => getGridSize(table).rows;
    const afterCornerResize = (table, origin, width, height) => {
      const location = getCornerLocation(origin);
      const isRightEdgeResize = endsWith(location, "e");
      const isNorthEdgeResize = startsWith(location, "n");
      if (startRawW === "") {
        convertToPercentSizeWidth(table);
      }
      if (startRawH === "") {
        convertToPixelSizeHeight(table);
      }
      if (width !== startW && startRawW !== "") {
        set$1(table, "width", startRawW);
        const resizing = lazyResizingBehaviour();
        const tableSize = lazySizing(table);
        const col = isPreserveTableColumnResizing(editor) || isRightEdgeResize ? getNumColumns(table) - 1 : 0;
        adjustWidth(table, width - startW, col, resizing, tableSize);
      } else if (isPercentage$1(startRawW)) {
        const percentW = parseFloat(startRawW.replace("%", ""));
        const targetPercentW = width * percentW / startW;
        set$1(table, "width", targetPercentW + "%");
      }
      if (isPixel(startRawW)) {
        syncTableCellPixels(table);
      }
      if (height !== startH && startRawH !== "") {
        set$1(table, "height", startRawH);
        const idx = isNorthEdgeResize ? 0 : getNumRows(table) - 1;
        adjustHeight(table, height - startH, idx);
      }
    };
    const destroy = () => {
      tableResize.on(sz => {
        sz.destroy();
      });
      resizeWire.on(w => {
        remove(editor, w);
      });
    };
    editor.on("init", () => {
      const rawWire = get(editor, isResizable);
      resizeWire.set(rawWire);
      if (hasTableObjectResizing(editor) && hasTableResizeBars(editor)) {
        const resizing = lazyResizingBehaviour();
        const sz = TableResize.create(rawWire, resizing, lazySizing);
        if (!editor.mode.isReadOnly()) {
          sz.on();
        }
        sz.events.startDrag.bind(_event => {
          selectionRng.set(editor.selection.getRng());
        });
        sz.events.beforeResize.bind(event => {
          const rawTable = event.table.dom;
          fireObjectResizeStart(editor, rawTable, getPixelWidth(rawTable), getPixelHeight(rawTable), barResizerPrefix + event.type);
        });
        sz.events.afterResize.bind(event => {
          const table = event.table;
          const rawTable = table.dom;
          removeDataStyle(table);
          selectionRng.on(rng => {
            editor.selection.setRng(rng);
            editor.focus();
          });
          fireObjectResized(editor, rawTable, getPixelWidth(rawTable), getPixelHeight(rawTable), barResizerPrefix + event.type);
          editor.undoManager.add();
        });
        tableResize.set(sz);
      }
    });
    editor.on("ObjectResizeStart", e => {
      const targetElm = e.target;
      if (isTable(targetElm) && !editor.mode.isReadOnly()) {
        const table = SugarElement.fromDom(targetElm);
        each$2(editor.dom.select(".mce-clonedresizable"), clone => {
          editor.dom.addClass(clone, "mce-" + getTableColumnResizingBehaviour(editor) + "-columns");
        });
        if (!isPixelSizing(table) && isTablePixelsForced(editor)) {
          convertToPixelSizeWidth(table);
        } else if (!isPercentSizing(table) && isTablePercentagesForced(editor)) {
          convertToPercentSizeWidth(table);
        }
        if (isNoneSizing(table) && startsWith(e.origin, barResizerPrefix)) {
          convertToPercentSizeWidth(table);
        }
        startW = e.width;
        startRawW = isTableResponsiveForced(editor) ? "" : getRawWidth(editor, targetElm).getOr("");
        startH = e.height;
        startRawH = getRawHeight(editor, targetElm).getOr("");
      }
    });
    editor.on("ObjectResized", e => {
      const targetElm = e.target;
      if (isTable(targetElm)) {
        const table = SugarElement.fromDom(targetElm);
        const origin = e.origin;
        if (isCornerResize(origin)) {
          afterCornerResize(table, origin, e.width, e.height);
        }
        removeDataStyle(table);
        fireTableModified(editor, table.dom, styleModified);
      }
    });
    const showResizeBars = () => {
      tableResize.on(resize => {
        resize.on();
        resize.showBars();
      });
    };
    const hideResizeBars = () => {
      tableResize.on(resize => {
        resize.off();
        resize.hideBars();
      });
    };
    editor.on("DisabledStateChange", e => {
      e.state ? hideResizeBars() : showResizeBars();
    });
    editor.on("SwitchMode", () => {
      editor.mode.isReadOnly() ? hideResizeBars() : showResizeBars();
    });
    editor.on("dragstart dragend", e => {
      e.type === "dragstart" ? hideResizeBars() : showResizeBars();
    });
    editor.on("remove", () => {
      destroy();
    });
    const refresh = table => {
      tableResize.on(resize => resize.refreshBars(SugarElement.fromDom(table)));
    };
    const hide = () => {
      tableResize.on(resize => resize.hideBars());
    };
    const show = () => {
      tableResize.on(resize => resize.showBars());
    };
    return {
      refresh,
      hide,
      show
    };
  };

  const setupTable = editor => {
    register(editor);
    const resizeHandler = TableResizeHandler(editor);
    const cellSelectionHandler = TableCellSelectionHandler(editor, resizeHandler);
    const actions = TableActions(editor, resizeHandler, cellSelectionHandler);
    registerCommands(editor, actions);
    registerQueryCommands(editor, actions);
    registerEvents(editor, actions);
    return {
      getSelectedCells: cellSelectionHandler.getSelectedCells,
      clearSelectedCells: cellSelectionHandler.clearSelectedCells
    };
  };

  const DomModel = editor => {
    const table = setupTable(editor);
    return { table };
  };
  var Model = () => {
    global$1.add("dom", DomModel);
  };

  Model();

})();
